Опубликован: 13.07.2012 | Уровень: специалист | Доступ: платный
Лекция 4:

Разработка собственных компонентов

< Лекция 3 || Лекция 4: 12 || Лекция 5 >
Аннотация: В этой лекции вы научитесь создавать собственные компоненты путём комбинации стандартных виджетов Juce в компоненте-контейнере.

Цель лекции: Научиться создавать произвольные компоненты на основе стандартных

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

Как правило, компонент интерфейса программы — это не просто область на экране, отображающая текст и / или изображение; это элемент, реагирующий на события системы и действия пользователя тем или иным образом (генерация сообщений другим компонентам, изменение внешнего вида и размера, выполнение вычислений и т.д. и т.п.). Взаимодействие подобных компонентов основывается на работе передатчиков событий (event broadcaster) и приёмников или слушателей событий (event listener).

Элементы интерфейса, имеющие стандартный внешний вид и выполняющие стандартные действия (кнопки, выпадающие меню и списки, флажки, переключатели и т.п.) называются виджетами — термин, пришедший из Linux. Конечно, "стандартный" внешний вид виджетов Juce — понятие относительное, поскольку, как упоминалось выше, библиотека не использует элементы управления целевой платформы, а рисует все элементы пользовательского интерфейса самостоятельно, пользуясь только системными функциями низкого уровня.

Слово "widget" является шуточным сокращением фразы "which it, gadget" - "эта, как её, штуковина". В Windows системах более привычным является название "элемент управления" (control). Виджеты включаются в компоненты-контейнеры, которые должны "слушать" события, генерируемые первыми, т.е. являться приёмниками (listener). Это достигается наследованием класса компонента-контейнера от нужного класса слушателя (ButtonListener, ComboBoxListener, SliderListener и т.п.). Классы слушателей являются абстрактными и содержат чистые виртуальные (pure virtual) функции, которые в классе компонента-контейнера должны быть переопределены.

Как правило, в качестве компонента-контейнера выступает упоминавшийся в предыдущей лекции компонент содержимого (content component), который предоставляет доступ к клиентской части окна приложения. Класс компонента содержимого наследуется от класса Component и класса (классов) слушателей.

Вообще, множественное наследование весьма характерно для Juce, поскольку довольно большое число классов библиотеки являются абстрактными. Так, например, в отличие от библиотеки Qt, в Juce невозможно создать таймер (объект класса Timer), поэтому для того, чтобы воспользоваться функциями класса, необходимо создать компонент, унаследовав его класс от классов Component и Timer.

Класс Component является основой для всех виджетов, включая окна и диалоги. Сам класс Component не является абстрактным, поэтому создание экземпляра данного класса вполне возможно. Однако в "чистом" виде объект класса Component бесполезен. В случае, если мы добавим в него какие-либо виджеты с помощью метода Component::addAndMakeVisible(Component* child, int zOrder = -1), код будет откомпилирован, однако на этапе выполнения возникнет ошибка, связанная с тем, что в Juce программист должен сам позаботиться об уничтожении дочерних виджетов при завершении работы программы, переопределив деструктор класса компонента-контейнера. Уничтожение дочерних виджетов достигается вызовом метода void Component::SafePointer<ComponentType>::deleteAndZero(), который удаляет из контейнера дочерний компонент и обнуляет указатель на него. Однако для того, чтобы не вызывать эту функцию для каждого из дочерних виджетов, проще воспользоваться функцией void Component::deleteAllChildren(), которая удаляет из контейнера все включённые в него компоненты.

Рассмотрим всё вышеизложенное на простом примере. Создадим собственный компонент, который будет включать в себя два дочерних виджета: ярлык (Label) и кнопку с текстом (TextButton). Мы унаследуем класс нашего компонента от ButtonListener для того, чтобы отслеживать щелчок пользователя по кнопке, и переопределим функцию virtual void Button::Listener::buttonClicked(Button*), которая будет вызывать метод класса приложения static void JUCEApplication::quit(), завершающий работу программы ( пример 4.1, пример 4.2).

#ifndef _TCentralComponent_h_
#define _TCentralComponent_h_
//---------------------------------------------------------------------
#include "../JuceLibraryCode/JuceHeader.h"
//---------------------------------------------------------------------
// Класс компонента содержимого.
// Наследует класс слушателя кнопок
class TCentralComponent : public Component,
              public ButtonListener
{
public:
  TCentralComponent();
  ~TCentralComponent();

  void paint(Graphics&);
  void resized();
  // Функция, отслеживающая щелчки по кнопке
  void buttonClicked(Button*);

private:
  Label* pHelloLabel;
  TextButton* pCloseButton;

  // Предотвращает создание копии конструктора и оператора =
  TCentralComponent(const TCentralComponent&);
  const TCentralComponent& operator= (const TCentralComponent&);
};
//---------------------------------------------------------------------
#endif
Листинг 4.1. Объявление класса компонента содержимого (файл TCentralComponent.h)
#include "TCentralComponent.h"
//---------------------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//---------------------------------------------------------------------
TCentralComponent::TCentralComponent() : Component("Central Component"),
                     pHelloLabel(0),
                     pCloseButton(0)
{
  pHelloLabel = new Label("Hello Label", tr("Привет, мир!"));
  addAndMakeVisible(pHelloLabel);
  pHelloLabel->setFont(Font(38.0000f, Font::bold));
  // Ориентация текста ярлыка - по центру виджета
  pHelloLabel->setJustificationType(Justification::centred);
  // Зпрет на редактирование содержимого ярлыка
  pHelloLabel->setEditable(false, false, false);
  // Синий цвет шрифта
  pHelloLabel->setColour(Label::textColourId, Colours::blue);
  pHelloLabel->setColour(TextEditor::backgroundColourId, Colours::azure);

  pCloseButton = new TextButton("Close Button");
  addAndMakeVisible(pCloseButton);
  pCloseButton->setButtonText(tr("Закрыть"));
  // Устанавливаем в качестве слушателя кнопки
  // сам компонент-контейнер
  pCloseButton->addListener(this);

  setSize (400, 200);
}
//---------------------------------------------------------------------
TCentralComponent::~TCentralComponent()
{
  // Удаляем дочерние виджеты
  // и обнуляем их указатели
  deleteAndZero(pHelloLabel);
  deleteAndZero(pCloseButton);
}
//---------------------------------------------------------------------
void TCentralComponent::paint(Graphics& Canvas)
{
  Canvas.fillAll(Colours::azure);
}
//---------------------------------------------------------------------
void TCentralComponent::resized()
{
  pHelloLabel->setBounds(0, 0, proportionOfWidth(1.0000f),
              proportionOfHeight(0.9000f));
  pCloseButton->setBounds(getWidth() - 20 - 100, getHeight() - 35, 100, 25);
}
//---------------------------------------------------------------------
void TCentralComponent::buttonClicked(Button* pButton)
{
  // Если нажата кнопка "Закрыть"...
  if(pButton == pCloseButton)
  {
    // выходим из программы
    JUCEApplication::quit();
  }
}
//---------------------------------------------------------------------
Листинг 4.2. Реализация класса компонента содержимого (файл TCentralComponent.cpp
< Лекция 3 || Лекция 4: 12 || Лекция 5 >
Иван Иванов
Иван Иванов
Россия
Александр Тырин
Александр Тырин
Россия