Разработка кроссплатформенных приложений с использованием Juce

Элементы управления. Кнопки

Разбить на страницы
Показывать лекцию целиком

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

Кнопки представляют, пожалуй, одну из наиболее часто используемых групп виджетов. Базовым классом для них является Button, которому наследуют классы кнопки с текстом TextButton (пример ее использования рассматривался нами в лекции 4 и многих последующих главах), кнопки со стрелкой ArrowButton, кнопок с изображениями (ImageButton, DrawableButton и ShapeButton), кнопки панели инструментов (ToolbarItemComponent), а также ряд компонентов, которые имеют по своим функциям лишь косвенное отношение к кнопкам: гиперссылка (HyperlinkButton), флажок (ToggleButton), который рассматривался нами в лекции 11 и вкладка (TabBarButton).

TextButton — это кнопка со стандартными (для Juce) формой и фоном и отображаемой на поверхности строкой текста. Кнопка TextButton может работать в двух режимах: простая управляющая кнопка (включено по умолчанию) и кнопка с фиксацией нажатого состояния. Для включения последнего режима необходимо вызвать метод void Button::setClickingTogglesState(bool shouldToggle) throw() с параметром true. Кроме того, возможно использование набора кнопок с фиксацией нажатого состояния, где в нажатом (on) состоянии может находиться одна и только одна кнопка. Для создания подобной группы используют методы void Button::setToggleState(bool shouldBeOn, bool sendChangeNotification) для задания режима фиксации и void Button::setRadioGroupId(int newGroupId) для включения кнопок в единую группу путем задания общего ID. Использование этих методов совершенно идентично таковому у ToggleButton (см. лекцию 11) .

Следует заметить, что, в отличие от других библиотек для построения графического интерфейса пользователя, в Juce кнопка в нажатом состоянии меняет только цвет фона. Цвета кнопки в нажатом (on) и отжатом (off) состоянии определяются значениями идентификаторов цвета перечислимого типа enum TextButton::ColourIds:

  • buttonColourId — цвет фона кнопки в ненажатом состоянии;
  • buttonOnColourId — цвет фона кнопки в нажатом состоянии;
  • textColourOffId — цвет текста кнопки в ненажатом состоянии;
  • textColourOnId — цвет текста кнопки в нажатом состоянии.
  • Задать необходимое значение цвета фона либо текста кнопки можно вызовом метода void Component::setColour(int colourId, const Colour colour), передав в качестве параметра один из идентификаторов ColourIds.

    Основное свойство с точки зрения внешнего вида кнопки с текстом — это, конечно же, надпись. Для работы с ней класс TextButton включает два метода, унаследованные от класса Button:

  • void Button::setButtonText(const String newText изменяет надпись на кнопке на newText
  • const String Button::getButtonText() const— возвращает текст, отображаемый на кнопке.
  • Как правило, программист сам заботится о том, чтобы размеры кнопки и отображаемой ею надписи соответствовали друг другу. Однако в случае, если надпись на кнопке меняется в процессе выполнения программы, может возникнуть необходимость "подогнать" размеры кнопки под новый текст. В этом поможет метод ).

    (рис 12.1) Программа, иллюстрирующая работу кнопки с текстом и изменяемой шириной

    При нажатии на кнопку надпись последней будет меняться на текст в поле ввода при его наличии. При этом ширина кнопки будет меняться в зависимости от размеров новой надписи, а высота будет оставаться прежней

     
    #include "TCentralComponent.h"
    //---------------------------------------------------------------------
    #define tr(s) String::fromUTF8(s)
    //---------------------------------------------------------------------
    TCentralComponent::TCentralComponent() : Component ("Central Component"),
                         pTextButton(0),
                         pTextEdit(0)
    {
      pTextButton = new TextButton("Text Button");
      pTextButton->addListener(this);
      pTextButton->setButtonText(tr("Введи новый текст и нажми кнопку"));
      addAndMakeVisible(pTextButton);
    
      pTextEdit = new TextEditor("Text Editor");
      pTextEdit->setReturnKeyStartsNewLine(false);
      pTextEdit->setText(String::empty);
      addAndMakeVisible(pTextEdit);
    
      setSize (400, 150);
    }
    //---------------------------------------------------------------------
    TCentralComponent::~TCentralComponent()
    {
      // Удаляем дочерние виджеты
      // и обнуляем их указатели
      deleteAllChildren();
    }
    //---------------------------------------------------------------------
    void TCentralComponent::paint(Graphics Canvas)
    {
      Canvas.fillAll(Colours::azure);
    }
    //---------------------------------------------------------------------
    void TCentralComponent::resized()
    {
      // Задаем стартовые размеры кнопки
      pTextButton->setBounds(20, 16, 240, 25);
      // "Подгоняет" размеры кнопки под отображаемую надпись
      pTextButton->changeWidthToFitText(-1);
      pTextEdit->setBounds(20, 56, getWidth() - 40, 25);
    }
    //---------------------------------------------------------------------
    void TCentralComponent::buttonClicked(Button* pButton)
    {
      // Если нажата кнопка...
      if(pButton == pTextButton)
      {
        // и поле ввода не пусто...
        if(!pTextEdit->getText().containsNonWhitespaceChars()) return;
        // в качестве надписи устанавливаем
        // текст из поля ввода
        pTextButton->setButtonText(pTextEdit->getText());
        // "Подгоняет" размеры кнопки под отображаемую надпись
        pTextButton->changeWidthToFitText(-1);
      }
    }
    //---------------------------------------------------------------------
    

    Обратите внимание, что метод changeWidthToFitText должен вызываться дважды: в обработчике щелчка по кнопке buttonClicked, и в функции resized; в противном случае при каждом изменении размеров окна, например, растягивании, кнопка будет возвращаться к начальной форме, заданной параметрами метода setBounds.

    Зачастую в надписи на кнопке один из символов подчеркнут. Это означает, что пользователь может нажать комбинацию клавиш <ALT>+<S>, где <S> - это подчеркнутый символ, и это будет эквивалентно щелчку по кнопке. Добиться этого можно с помощью метода void Button::addShortcut(const KeyPress key), где key — код "горячей" клавиши с или без модификатора (<CTRL>, <ALT>, <SHIFT> и т.п.). Например, если нам нужно, чтобы обработчик щелчка по кнопке pCloseButton срабатывал при нажатии клавиш <ALT>+<Q>, необходимо вызвать метод addShortcut следующим образом:

    pCloseButton->addShortcut(KeyPress('q', ModifierKeys::altModifier, 0))
    

    В этом примере мы создали новый объект класса KeyPress, отслеживающего нажатие клавиш клавиатуры, в конструктор которого в качестве параметров передали код "горячей" клавиши ('q') и ее модификатора. Третий параметр — печатаемый символ (в нашем случае равен нулю, т.к. вызывающий виджет не является полем ввода).

    В свою очередь, созданный объект класса KeyPress передается в метод addShortcut в качестве параметра. Ниже приведены некоторые идентификаторы перечислимого типа enum ModifierKeys:

  • shiftModifier — флаг нажатия на клавишу <SHIFT>;
  • ctrlModifier — флаг нажатия на клавишу <CTRL>;
  • altModifier — флаг нажатия на клавишу <ALT>.
  • Помимо этого, ModifierKeys содержит модификаторы, связанные с различными манипуляциями кнопками мыши.

    Разумеется, можно зарегистрировать для кнопки "горячую" клавишу безо всякого модификатора. Например, если в качестве параметра в метод addShortcut кнопки "Отменить" передать в качестве параметра KeyPress::escapeKey, то нажатие пользователем клавиши <ESC> будет эквивалентно щелчку по данной кнопке. Это целесообразно делать в различных диалоговых окнах, чтобы пользователь мог их закрывать без использования мыши.

    Для того, чтобы отключить все "горячие" клавиши, зарегистрированные для данной кнопки, используют метод void Button::clearShortcuts().

    Основное событие любой кнопки — это щелчок по ней. При этом вызывается виртуальный защищенный метод ). Метод является чистой виртуальной (pure virtual) функцией и должен быть обязательно переопределен в производном классе.

    Практически все, сказанное выше, относится и к другим кнопкам.

    ).

    (рис 12.2) Внешний вид кнопки со стрелкой (параметр конструктора arrowDirection принимает значение 0.75; arrowColour — Colours::blue)

    Конструктор кнопки ArrowButton::ArrowButton(const String buttonName, float arrowDirection, const Colour arrowColour) принимает три параметра:

  • buttonName — имя кнопки;
  • arrowDirection — направление кнопки, где при принимаемом значении 0.0 стрелка направлена вправо, 0.25 — вниз, 0.5 — влево, а 0.75 — вверх;
  • arrowColour — цвет стрелки.
  • DrawableButton — это кнопка, которая отображает один или несколько объектов класса, производного от Drawable. Drawable — это базовый класс для объектов, которые могут рисовать сами на себе графические примитивы (полигоны) или изображения (например, загруженные из файла). Поскольку Drawable является абстрактным классом, включающим ряд чистых виртуальных функций-членов, на практике используют производный от него класс, DrawableImage объект которого представляет собой битовое изображение (bitmap).

    При необходимости возможно использование других производных от Drawable классов, например, DrawableShape или DrawableText.

    Конструктор DrawableButton::DrawableButton(const String buttonName, ButtonStyle buttonStyle) создает "пустую", т.е. не содержащую никаких изображений кнопку.

    В качестве параметров конструктор принимает строку — имя кнопки (buttonName) и ее стиль, идентификатор перечислимого типа enum DrawableButton::ButtonStyle.

    Стили у DrawableButton могут быть следующими:

  • ImageFitted — кнопка отображает объекты Drawable, изменяя их размеры таким образом, чтобы изображения помещались внутри кнопки;
  • ImageRaw — кнопка показывает изображения с их исходными размерами;
  • ImageOnButtonBackground — рисуется стандартная для Juce кнопка с наложенным поверх изображением Drawable.
  • Следует помнить, что программист должен сам позаботиться о соответствии размеров изображения, отображаемого DrawableImage и размеров кнопки DrawableButton.

    Рассмотрим использование ).

    (рис 12.3) Изображения для кнопки DrawableButton, подготовленные в GIMP и cохраненные в виде файлов *.png

    В качестве основы для нашей программы используем код из вышеприведенного примера, заменив класс у .

    #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::textColourId, Colours::black);
      pHelloLabel->setColour(TextEditor::backgroundColourId, Colours::azure);
    
      // Получаем рабочий каталог программы и сохраняем путь к нему
      // в виде строки
      String sPath = File::getCurrentWorkingDirectory().getFullPathName();
      // Полное имя файла изображения ненажатой кнопки
      String sUp;
      // Полное имя файла изображения нажатой кнопки
      String sDown;
    
      // Получаем полные имена файлов изображений для Windows и Linux
      #if JUCE_WINDOWS
        sUp = sPath + "\\gimpbutton-up.png";
        sDown = sPath + "\\gimpbutton-down.png";
      #elif JUCE_LINUX
        sUp = sPath + "/gimpbutton-up.png";
        sDown = sPath + "/gimpbutton-down.png";
      #endif
    
      // Изображение для ненажатой кнопки
      DrawableImage UpImage;
      // Изображение для нажатой кнопки
      DrawableImage DownImage;
      // Загружаем изображение в DrawableImage из соответствующего файла
      UpImage.setImage(ImageCache::getFromFile(File(sUp)));
      DownImage.setImage(ImageCache::getFromFile(File(sDown)));
    
      // Создаем пустую DrawableButton
      pCloseButton = new DrawableButton("CloseButton",
                        DrawableButton::ImageAboveTextLabel);
      // Устанавливаем изображения для различных состояний кнопки
      pCloseButton->setImages( UpImage,  UpImage,  DownImage);
      addAndMakeVisible(pCloseButton);
      pCloseButton->setButtonText(tr("Закрыть"));
      pCloseButton->addListener(this);
    
      setSize (400, 200);
    }
    //---------------------------------------------------------------------
    TCentralComponent::~TCentralComponent()
    {
      deleteAllChildren();
    }
    //---------------------------------------------------------------------
    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() - 10 - 204, getHeight() - 60, 204, 53);
    }
    //---------------------------------------------------------------------
    void TCentralComponent::buttonClicked(Button* pButton)
    {
      if (pButton == pCloseButton)
      {
        JUCEApplication::quit();
      }
    }
    //---------------------------------------------------------------------
    

    В конструкторе класса компонента содержимого мы создаем "пустую" кнопку DrawableButton, которая будет показывать надпись "Закрыть" под отображаемыми картинками.

    Для того, чтобы наша кнопка pCloseButton могла показывать изображения, созданные в GIMP, мы должны получить их из объектов класса DrawableImage с помощью метода

    void DrawableButton::setImages(
      const Drawable* normalImage,
      const Drawable* overImage = 0,
      const Drawable* downImage = 0,
      const Drawable* disabledImage = 0,
      const Drawable* normalImageOn = 0,
      const Drawable* overImageOn = 0,
      const Drawable* downImageOn = 0,
      const Drawable* disabledImageOn = 0 
    ).
    

    Смысл передаваемых параметров понятен из их названий:

  • normalImage — изображение кнопки в ее "нормальном", ненажатом состоянии. Единственный параметр, значение которого не может быть равным нулю;
  • overImage — изображение кнопки при наведении на нее мыши. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage;
  • downImage — изображение кнопки в нажатом состоянии. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение overImage;
  • disabledImage — изображение кнопки в недоступном состоянии. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage с увеличенной прозрачностью;
  • normalImageOn — то же самое, что и normalImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage;
  • overImageOn — то же самое, что и overImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImageOn;
  • downImageOn — то же самое, что и downImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение overImageOn;
  • disabledImageOn — то же самое, что и disabledImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано нормальное изображение с увеличенной прозрачностью.
  • Поскольку мы подготовили лишь два файла изображений для кнопки рис 12.3, то, соответственно, мы будем передавать в метод setImages лишь два объекта класса DrawableImae — UpImage для ненажатой и DownImage — для нажатой кнопки.

    Загружать изображения из графических файлов в объекты .

    В свою очередь, метод getFromFile в качестве параметра получает объект класса File, предназначенного для манипуляций с локальными файлами и директориями. В качестве параметра конструктора объект класса получает строку, содержащую абсолютный путь к файлу.

    Чтобы сформировать эту строку, мы получаем вначале текущую рабочую папку нашей программы (напомним, что файлы загружаемых изображений должны находиться именно в ней) с помощью метода .

    Внешний вид работающей программы показан на рисунке 12.4 .

    (рис 12.4) Программа, иллюстрирующая работу DrawableButton

    Наша программа имеет ряд мелких, но существенных недостатков: во-первых, есть риск что используемые файлы изображений могут быть удалены по ошибке, а во-вторых, метод getCurrentWorkingDirectory() может возвращать разные значения в зависимости от условий запуска исполняемого файла нашей программы: например, в в Windows при запуске для отладки в среде разработки; в Linux — при запуске не из командной строки, а щелчком мышью по значку файла. Все это, хоть и не вызовет краха программы, но приведет к неправильному отображению закрывающей кнопки.

    Поэтому удобнее, если загружаемые изображения будут "зашиты" в самом исполняемом файле программы.

    Добавлять те или иные изображения в программу в виде платформо-независимых ресурсов в Juce можно двумя способами: во-первых, если вы остановились на визуальном проектировании компонента, можно воспользоваться возможностями jucer и, во-вторых, можно использовать консольную утилиту BinaryBuilder, входящую в поставку Juce.

    В первом случае бинарные ресурсы, описывающие изображения, будут добавлены в класс создаваемого компонента в виде его членов, а во втором — для них будет создано отдельное пространство имен (namespace).

    Для того, чтобы добавить бинарный ресурс в класс компонента, нужно в jucer создать новый компонент (). В объявление класса будут добавлены две переменнные (static const char* и static const int), описывающие само изображение и его размер, и соответствующие имени ресурса, отображаемого на вкладке jucer. Например, если вы выбрали изображение picture.png, то название ресурса будет picture_png, а имена переменных static const char* picture_png и static const int picture_pngSize.

    (рис 12.5) Добавление бинарного ресурса в jucer

    Для того, чтобы добавить бинарные ресурсы в виде отдельного пространства имен, как упоминалось выше, необходимо использовать консольную утилиту BinaryBuilder. Она находится в папке <каталог Juce>/juce/extras/binarybuilder. Как и другие утилиты, входящие в поставку juce, BinaryBuilder распространяется в виде исходных текстов, поэтому программу требуется предварительно откомпилировать.

    Запущенная без параметров, утилита BinaryBuilder выводит краткую справку по своему использованию. Запускать программу следует со следующими параметрами:

    BinaryBuilder  sourcedirectory targetdirectory targetclassnam
    

    где

  • sourcedirectory — папка, в которой находятся добавляемые изображения или другие бинарные ресурсы;
  • targetdirectory — папка, в которой будут сохранены генерируемые файлы исходных текстов;
  • targetclassname — название пространства имен бинарных ресурсов.
  • И в том, и в другом случае бинарные данные графического файла переводятся в форму обычного массива C++ .

    static const unsigned char ucGimpbuttondown_png[] = 
    {137,80,78,71,13,10,26,10,0,0 ...
    

    В случае использования juce этот массив добавляется в конец реализации класса компонента содержимого, а в случае использования BinaryBuilder — в файл реализации пространства имен.

    Переменная , ).

    const char* TImagesFactory::psGimpbuttondown_png = 
    (const char*)ucGimpbuttondown_png;
    

    Рассмотрим использование бинарных ресурсов для создания . Как видно из листинга 12.5, реализация класса компонента содержимого практически не изменилась: мы лишь подключили пространство имен TImagesFactory и воспользовались методом static const Image ImageCache::getFromMemory(const void* imageData, int dataSize) для загрузки изображений в DrawableImage не из графического файла, а из памяти.

    #include "TCentralComponent.h"
    // Подключаем пространство имен TImagesFactory
    #include "TImagesFactory.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::textColourId, Colours::black);
      pHelloLabel->setColour(TextEditor::backgroundColourId, Colours::azure);
    
      // Изображение для ненажатой кнопки
      DrawableImage UpImage;
      // Изображение для нажатой кнопки
      DrawableImage DownImage;
      // Загружаем изображение в DrawableImage из памяти
      UpImage.setImage(ImageCache::getFromMemory(
                        TImagesFactory::psGimpbuttonup_png,
                        TImagesFactory::iGimpbuttonup_pngSize));
      DownImage.setImage(ImageCache::getFromMemory(
                       TImagesFactory::psGimpbuttondown_png,
                       TImagesFactory::iGimpbuttondown_pngSize));
    
      // Создаем пустую DrawableButton
      pCloseButton = new DrawableButton("CloseButton", 
                       DrawableButton::ImageAboveTextLabel);
      // Устанавливаем изображения для различных состояний кнопки
      pCloseButton->setImages( UpImage,  UpImage,  DownImage);
      addAndMakeVisible(pCloseButton);
      pCloseButton->setButtonText(tr("Закрыть"));
      pCloseButton->addListener(this);
    
      setSize (400, 200);
    }
    //---------------------------------------------------------------------
    

    Откомпилируем новую версию программы и убедимся, что по своему виду и функциональности она ничем не отличается от предыдущей рис 12.4, но лишена ее недостатков.

    Как уже отмечалось выше, с помощью ряда классов — потомков DrawableDrawableButton может отображать не только готовые изображения, но и различные графические примитивы. Пример создания сложных кнопок DrawableButton в виде звезд с произвольным числом лучей с использованием класса DrawablePath входит в поставку Juce (см. <каталог Juce>/juce/extras/JuceDemo/Source/demos, файл WidgetsDemo.cpp).

    В данном примере DrawableButton легко можно заменить на ShapeButton — кнопку, содержащую залитую геометрическую фигуру (shape). Установить эту фигуру можно с помощью метода void ShapeButton::setShape(const Path newShape, bool resizeNowToFitThisShape, bool maintainShapeProportions, bool hasDropShadow), где newShape — собственно, рисуемая фигура (объект класса Path), а maintainShapeProportions и hasDropShadow — флаги того, должны ли размеры кнопки подстраиваться под размеры фигуры, и того, должна ли фигура отбрасывать тень, соответственно.

    Мы не будем подробно останавливаться на работе с ImageButton, поскольку она сходна с таковой для DrawableButton, разница лишь в использовании объектов класса Image, а не Drawable в методе setImages плюс задание цветов кнопки при манипуляции с ней мышью (наведение и т.п.). Пример использования ImageButton имеется в демонстрационном приложении Juce (см. <каталог Juce>/juce/extras/JuceDemo/Source/demos, файл WidgetsDemo.cpp).

    ToolbarItcodeComponent — кнопка инструментальной панели — подробно рассмотрена в лекции 15 (комоненты панели инструментов).

    HyperlinkButton — кнопка, отображающая подчеркнутую гиперссылку на ресурс Web, который запускается в браузере, установленном по умолчанию, при щелчке по ней. Особенностью кнопки является то, что для ее работы не нужен слушатель (ButtonListener), хотя его установка и возможна, например, если вы хотите, чтобы были выполнены какие-либо действия перед переходом по ссылке.

    Конструктор класса кнопки с гипертекстовой ссылкой HyperlinkButton::HyperlinkButton(const String linkText, const URL linkURL) принимает два параметра: linkText — отображаемый текст и linkURL — собственно гиперссылка, объект специального класса Juce, URL.

    Ссылку на ресурс Web можно изменить с помощью метода void HyperlinkButton::setURL(const URL newURL ) throw().

    Как и у любой кнопки, текст HyperlinkButton можно изменить методом setButtonText.

    Пример использования HyperlinkButton представлен в листинге 12.6, внешний вид работающей программы показан на рисунке12.6

    #include "TCentralComponent.h"
    //---------------------------------------------------------------------
    #define tr(s) String::fromUTF8(s)
    //---------------------------------------------------------------------
    TCentralComponent::TCentralComponent() : Component("Central Component"),
                         pHyperlinkButton(0)
    {
      // Кнопка с гиперссылкой
      pHyperlinkButton = 
      new HyperlinkButton(L"http://www.rawmaterialsoftware.com/juce.php",
                URL(L"http://www.rawmaterialsoftware.com/juce.php"));
    JuceHyperlinkButton  // Устанавливаем текст всплывающей подсказки
      pHyperlinkButton->setTooltip(
                    L"http://www.rawmaterialsoftware.com/juce.php");
      addAndMakeVisible(pHyperlinkButton);
    
      setSize(400, 200);
    }
    //---------------------------------------------------------------------
    TCentralComponent::~TCentralComponent()
    {
      // Удаляем дочерний виджет
      // и обнуляем его указатель
      deleteAndZero(pHyperlinkButton);
    }
    //---------------------------------------------------------------------
    void TCentralComponent::paint(Graphics Canvas)
    {
      Canvas.fillAll(Colours::white);
    }
    //---------------------------------------------------------------------
    void TCentralComponent::resized()
    {
      pHyperlinkButton->setBounds(15, proportionOfHeight(0.4500f), 
                    getWidth() - 15, 25);
    }
    //---------------------------------------------------------------------
    
    (рис 12.6) Внешний вид программы с HyperlinkButton

    Краткие итоги

    В данной лекции нами рассмотрены классы, необходимые для создания кнопок различного вида: кнопок с текстом TextButton, кнопок со стрелкой ArrowButton, кнопок с изображениями (ImageButton, DrawableButton и ShapeButton) и гиперссылки (HyperlinkButton). Базовым классом для всех кнопок является Button.

    Упражнение

    Напишите игру "Светофор": при нажатии на одну из трех кнопок, расположенных вертикально, она окрашивается в соответствующий цвет (красный, желтый или зеленый), а остальные — в темно-серый.

    Дополнительные материалы

    Архив с исходными текстами примеров Вы можете скачать здесь

    Страницы:

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

    Кнопки представляют, пожалуй, одну из наиболее часто используемых групп виджетов. Базовым классом для них является Button, которому наследуют классы кнопки с текстом TextButton (пример ее использования рассматривался нами в лекции 4 и многих последующих главах), кнопки со стрелкой ArrowButton, кнопок с изображениями (ImageButton, DrawableButton и ShapeButton), кнопки панели инструментов (ToolbarItemComponent), а также ряд компонентов, которые имеют по своим функциям лишь косвенное отношение к кнопкам: гиперссылка (HyperlinkButton), флажок (ToggleButton), который рассматривался нами в лекции 11 и вкладка (TabBarButton).

    TextButton — это кнопка со стандартными (для Juce) формой и фоном и отображаемой на поверхности строкой текста. Кнопка TextButton может работать в двух режимах: простая управляющая кнопка (включено по умолчанию) и кнопка с фиксацией нажатого состояния. Для включения последнего режима необходимо вызвать метод void Button::setClickingTogglesState(bool shouldToggle) throw() с параметром true. Кроме того, возможно использование набора кнопок с фиксацией нажатого состояния, где в нажатом (on) состоянии может находиться одна и только одна кнопка. Для создания подобной группы используют методы void Button::setToggleState(bool shouldBeOn, bool sendChangeNotification) для задания режима фиксации и void Button::setRadioGroupId(int newGroupId) для включения кнопок в единую группу путем задания общего ID. Использование этих методов совершенно идентично таковому у ToggleButton (см. лекцию 11) .

    Следует заметить, что, в отличие от других библиотек для построения графического интерфейса пользователя, в Juce кнопка в нажатом состоянии меняет только цвет фона. Цвета кнопки в нажатом (on) и отжатом (off) состоянии определяются значениями идентификаторов цвета перечислимого типа enum TextButton::ColourIds:

  • buttonColourId — цвет фона кнопки в ненажатом состоянии;
  • buttonOnColourId — цвет фона кнопки в нажатом состоянии;
  • textColourOffId — цвет текста кнопки в ненажатом состоянии;
  • textColourOnId — цвет текста кнопки в нажатом состоянии.
  • Задать необходимое значение цвета фона либо текста кнопки можно вызовом метода void Component::setColour(int colourId, const Colour colour), передав в качестве параметра один из идентификаторов ColourIds.

    Основное свойство с точки зрения внешнего вида кнопки с текстом — это, конечно же, надпись. Для работы с ней класс TextButton включает два метода, унаследованные от класса Button:

  • void Button::setButtonText(const String newText изменяет надпись на кнопке на newText
  • const String Button::getButtonText() const— возвращает текст, отображаемый на кнопке.
  • Как правило, программист сам заботится о том, чтобы размеры кнопки и отображаемой ею надписи соответствовали друг другу. Однако в случае, если надпись на кнопке меняется в процессе выполнения программы, может возникнуть необходимость "подогнать" размеры кнопки под новый текст. В этом поможет метод ).

    (рис 12.1) Программа, иллюстрирующая работу кнопки с текстом и изменяемой шириной

    При нажатии на кнопку надпись последней будет меняться на текст в поле ввода при его наличии. При этом ширина кнопки будет меняться в зависимости от размеров новой надписи, а высота будет оставаться прежней

     
    #include "TCentralComponent.h"
    //---------------------------------------------------------------------
    #define tr(s) String::fromUTF8(s)
    //---------------------------------------------------------------------
    TCentralComponent::TCentralComponent() : Component ("Central Component"),
                         pTextButton(0),
                         pTextEdit(0)
    {
      pTextButton = new TextButton("Text Button");
      pTextButton->addListener(this);
      pTextButton->setButtonText(tr("Введи новый текст и нажми кнопку"));
      addAndMakeVisible(pTextButton);
    
      pTextEdit = new TextEditor("Text Editor");
      pTextEdit->setReturnKeyStartsNewLine(false);
      pTextEdit->setText(String::empty);
      addAndMakeVisible(pTextEdit);
    
      setSize (400, 150);
    }
    //---------------------------------------------------------------------
    TCentralComponent::~TCentralComponent()
    {
      // Удаляем дочерние виджеты
      // и обнуляем их указатели
      deleteAllChildren();
    }
    //---------------------------------------------------------------------
    void TCentralComponent::paint(Graphics Canvas)
    {
      Canvas.fillAll(Colours::azure);
    }
    //---------------------------------------------------------------------
    void TCentralComponent::resized()
    {
      // Задаем стартовые размеры кнопки
      pTextButton->setBounds(20, 16, 240, 25);
      // "Подгоняет" размеры кнопки под отображаемую надпись
      pTextButton->changeWidthToFitText(-1);
      pTextEdit->setBounds(20, 56, getWidth() - 40, 25);
    }
    //---------------------------------------------------------------------
    void TCentralComponent::buttonClicked(Button* pButton)
    {
      // Если нажата кнопка...
      if(pButton == pTextButton)
      {
        // и поле ввода не пусто...
        if(!pTextEdit->getText().containsNonWhitespaceChars()) return;
        // в качестве надписи устанавливаем
        // текст из поля ввода
        pTextButton->setButtonText(pTextEdit->getText());
        // "Подгоняет" размеры кнопки под отображаемую надпись
        pTextButton->changeWidthToFitText(-1);
      }
    }
    //---------------------------------------------------------------------
    

    Обратите внимание, что метод changeWidthToFitText должен вызываться дважды: в обработчике щелчка по кнопке buttonClicked, и в функции resized; в противном случае при каждом изменении размеров окна, например, растягивании, кнопка будет возвращаться к начальной форме, заданной параметрами метода setBounds.

    Зачастую в надписи на кнопке один из символов подчеркнут. Это означает, что пользователь может нажать комбинацию клавиш <ALT>+<S>, где <S> - это подчеркнутый символ, и это будет эквивалентно щелчку по кнопке. Добиться этого можно с помощью метода void Button::addShortcut(const KeyPress key), где key — код "горячей" клавиши с или без модификатора (<CTRL>, <ALT>, <SHIFT> и т.п.). Например, если нам нужно, чтобы обработчик щелчка по кнопке pCloseButton срабатывал при нажатии клавиш <ALT>+<Q>, необходимо вызвать метод addShortcut следующим образом:

    pCloseButton->addShortcut(KeyPress('q', ModifierKeys::altModifier, 0))
    

    В этом примере мы создали новый объект класса KeyPress, отслеживающего нажатие клавиш клавиатуры, в конструктор которого в качестве параметров передали код "горячей" клавиши ('q') и ее модификатора. Третий параметр — печатаемый символ (в нашем случае равен нулю, т.к. вызывающий виджет не является полем ввода).

    В свою очередь, созданный объект класса KeyPress передается в метод addShortcut в качестве параметра. Ниже приведены некоторые идентификаторы перечислимого типа enum ModifierKeys:

  • shiftModifier — флаг нажатия на клавишу <SHIFT>;
  • ctrlModifier — флаг нажатия на клавишу <CTRL>;
  • altModifier — флаг нажатия на клавишу <ALT>.
  • Помимо этого, ModifierKeys содержит модификаторы, связанные с различными манипуляциями кнопками мыши.

    Разумеется, можно зарегистрировать для кнопки "горячую" клавишу безо всякого модификатора. Например, если в качестве параметра в метод addShortcut кнопки "Отменить" передать в качестве параметра KeyPress::escapeKey, то нажатие пользователем клавиши <ESC> будет эквивалентно щелчку по данной кнопке. Это целесообразно делать в различных диалоговых окнах, чтобы пользователь мог их закрывать без использования мыши.

    Для того, чтобы отключить все "горячие" клавиши, зарегистрированные для данной кнопки, используют метод void Button::clearShortcuts().

    Основное событие любой кнопки — это щелчок по ней. При этом вызывается виртуальный защищенный метод ). Метод является чистой виртуальной (pure virtual) функцией и должен быть обязательно переопределен в производном классе.

    Практически все, сказанное выше, относится и к другим кнопкам.

    ).

    (рис 12.2) Внешний вид кнопки со стрелкой (параметр конструктора arrowDirection принимает значение 0.75; arrowColour — Colours::blue)

    Конструктор кнопки ArrowButton::ArrowButton(const String buttonName, float arrowDirection, const Colour arrowColour) принимает три параметра:

  • buttonName — имя кнопки;
  • arrowDirection — направление кнопки, где при принимаемом значении 0.0 стрелка направлена вправо, 0.25 — вниз, 0.5 — влево, а 0.75 — вверх;
  • arrowColour — цвет стрелки.
  • DrawableButton — это кнопка, которая отображает один или несколько объектов класса, производного от Drawable. Drawable — это базовый класс для объектов, которые могут рисовать сами на себе графические примитивы (полигоны) или изображения (например, загруженные из файла). Поскольку Drawable является абстрактным классом, включающим ряд чистых виртуальных функций-членов, на практике используют производный от него класс, DrawableImage объект которого представляет собой битовое изображение (bitmap).

    При необходимости возможно использование других производных от Drawable классов, например, DrawableShape или DrawableText.

    Конструктор DrawableButton::DrawableButton(const String buttonName, ButtonStyle buttonStyle) создает "пустую", т.е. не содержащую никаких изображений кнопку.

    В качестве параметров конструктор принимает строку — имя кнопки (buttonName) и ее стиль, идентификатор перечислимого типа enum DrawableButton::ButtonStyle.

    Стили у DrawableButton могут быть следующими:

  • ImageFitted — кнопка отображает объекты Drawable, изменяя их размеры таким образом, чтобы изображения помещались внутри кнопки;
  • ImageRaw — кнопка показывает изображения с их исходными размерами;
  • ImageOnButtonBackground — рисуется стандартная для Juce кнопка с наложенным поверх изображением Drawable.
  • Следует помнить, что программист должен сам позаботиться о соответствии размеров изображения, отображаемого DrawableImage и размеров кнопки DrawableButton.

    Рассмотрим использование ).

    (рис 12.3) Изображения для кнопки DrawableButton, подготовленные в GIMP и cохраненные в виде файлов *.png

    В качестве основы для нашей программы используем код из вышеприведенного примера, заменив класс у .

    #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::textColourId, Colours::black);
      pHelloLabel->setColour(TextEditor::backgroundColourId, Colours::azure);
    
      // Получаем рабочий каталог программы и сохраняем путь к нему
      // в виде строки
      String sPath = File::getCurrentWorkingDirectory().getFullPathName();
      // Полное имя файла изображения ненажатой кнопки
      String sUp;
      // Полное имя файла изображения нажатой кнопки
      String sDown;
    
      // Получаем полные имена файлов изображений для Windows и Linux
      #if JUCE_WINDOWS
        sUp = sPath + "\\gimpbutton-up.png";
        sDown = sPath + "\\gimpbutton-down.png";
      #elif JUCE_LINUX
        sUp = sPath + "/gimpbutton-up.png";
        sDown = sPath + "/gimpbutton-down.png";
      #endif
    
      // Изображение для ненажатой кнопки
      DrawableImage UpImage;
      // Изображение для нажатой кнопки
      DrawableImage DownImage;
      // Загружаем изображение в DrawableImage из соответствующего файла
      UpImage.setImage(ImageCache::getFromFile(File(sUp)));
      DownImage.setImage(ImageCache::getFromFile(File(sDown)));
    
      // Создаем пустую DrawableButton
      pCloseButton = new DrawableButton("CloseButton",
                        DrawableButton::ImageAboveTextLabel);
      // Устанавливаем изображения для различных состояний кнопки
      pCloseButton->setImages( UpImage,  UpImage,  DownImage);
      addAndMakeVisible(pCloseButton);
      pCloseButton->setButtonText(tr("Закрыть"));
      pCloseButton->addListener(this);
    
      setSize (400, 200);
    }
    //---------------------------------------------------------------------
    TCentralComponent::~TCentralComponent()
    {
      deleteAllChildren();
    }
    //---------------------------------------------------------------------
    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() - 10 - 204, getHeight() - 60, 204, 53);
    }
    //---------------------------------------------------------------------
    void TCentralComponent::buttonClicked(Button* pButton)
    {
      if (pButton == pCloseButton)
      {
        JUCEApplication::quit();
      }
    }
    //---------------------------------------------------------------------
    

    В конструкторе класса компонента содержимого мы создаем "пустую" кнопку DrawableButton, которая будет показывать надпись "Закрыть" под отображаемыми картинками.

    Для того, чтобы наша кнопка pCloseButton могла показывать изображения, созданные в GIMP, мы должны получить их из объектов класса DrawableImage с помощью метода

    void DrawableButton::setImages(
      const Drawable* normalImage,
      const Drawable* overImage = 0,
      const Drawable* downImage = 0,
      const Drawable* disabledImage = 0,
      const Drawable* normalImageOn = 0,
      const Drawable* overImageOn = 0,
      const Drawable* downImageOn = 0,
      const Drawable* disabledImageOn = 0 
    ).
    

    Смысл передаваемых параметров понятен из их названий:

  • normalImage — изображение кнопки в ее "нормальном", ненажатом состоянии. Единственный параметр, значение которого не может быть равным нулю;
  • overImage — изображение кнопки при наведении на нее мыши. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage;
  • downImage — изображение кнопки в нажатом состоянии. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение overImage;
  • disabledImage — изображение кнопки в недоступном состоянии. В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage с увеличенной прозрачностью;
  • normalImageOn — то же самое, что и normalImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImage;
  • overImageOn — то же самое, что и overImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение normalImageOn;
  • downImageOn — то же самое, что и downImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано изображение overImageOn;
  • disabledImageOn — то же самое, что и disabledImage, но используется в том случае, если кнопка с фиксацией нажатого состояния находится в режиме "включено" (on). В случае, если параметр принимает нулевое значение, для отрисовки этого состояния будет использовано нормальное изображение с увеличенной прозрачностью.
  • Поскольку мы подготовили лишь два файла изображений для кнопки рис 12.3, то, соответственно, мы будем передавать в метод setImages лишь два объекта класса DrawableImae — UpImage для ненажатой и DownImage — для нажатой кнопки.

    Загружать изображения из графических файлов в объекты .

    В свою очередь, метод getFromFile в качестве параметра получает объект класса File, предназначенного для манипуляций с локальными файлами и директориями. В качестве параметра конструктора объект класса получает строку, содержащую абсолютный путь к файлу.

    Чтобы сформировать эту строку, мы получаем вначале текущую рабочую папку нашей программы (напомним, что файлы загружаемых изображений должны находиться именно в ней) с помощью метода .

    Внешний вид работающей программы показан на рисунке 12.4 .

    (рис 12.4) Программа, иллюстрирующая работу DrawableButton

    Наша программа имеет ряд мелких, но существенных недостатков: во-первых, есть риск что используемые файлы изображений могут быть удалены по ошибке, а во-вторых, метод getCurrentWorkingDirectory() может возвращать разные значения в зависимости от условий запуска исполняемого файла нашей программы: например, в в Windows при запуске для отладки в среде разработки; в Linux — при запуске не из командной строки, а щелчком мышью по значку файла. Все это, хоть и не вызовет краха программы, но приведет к неправильному отображению закрывающей кнопки.

    Поэтому удобнее, если загружаемые изображения будут "зашиты" в самом исполняемом файле программы.

    Добавлять те или иные изображения в программу в виде платформо-независимых ресурсов в Juce можно двумя способами: во-первых, если вы остановились на визуальном проектировании компонента, можно воспользоваться возможностями jucer и, во-вторых, можно использовать консольную утилиту BinaryBuilder, входящую в поставку Juce.

    В первом случае бинарные ресурсы, описывающие изображения, будут добавлены в класс создаваемого компонента в виде его членов, а во втором — для них будет создано отдельное пространство имен (namespace).

    Для того, чтобы добавить бинарный ресурс в класс компонента, нужно в jucer создать новый компонент (). В объявление класса будут добавлены две переменнные (static const char* и static const int), описывающие само изображение и его размер, и соответствующие имени ресурса, отображаемого на вкладке jucer. Например, если вы выбрали изображение picture.png, то название ресурса будет picture_png, а имена переменных static const char* picture_png и static const int picture_pngSize.

    (рис 12.5) Добавление бинарного ресурса в jucer

    Для того, чтобы добавить бинарные ресурсы в виде отдельного пространства имен, как упоминалось выше, необходимо использовать консольную утилиту BinaryBuilder. Она находится в папке <каталог Juce>/juce/extras/binarybuilder. Как и другие утилиты, входящие в поставку juce, BinaryBuilder распространяется в виде исходных текстов, поэтому программу требуется предварительно откомпилировать.

    Запущенная без параметров, утилита BinaryBuilder выводит краткую справку по своему использованию. Запускать программу следует со следующими параметрами:

    BinaryBuilder  sourcedirectory targetdirectory targetclassnam
    

    где

  • sourcedirectory — папка, в которой находятся добавляемые изображения или другие бинарные ресурсы;
  • targetdirectory — папка, в которой будут сохранены генерируемые файлы исходных текстов;
  • targetclassname — название пространства имен бинарных ресурсов.
  • И в том, и в другом случае бинарные данные графического файла переводятся в форму обычного массива C++ .

    static const unsigned char ucGimpbuttondown_png[] = 
    {137,80,78,71,13,10,26,10,0,0 ...
    

    В случае использования juce этот массив добавляется в конец реализации класса компонента содержимого, а в случае использования BinaryBuilder — в файл реализации пространства имен.

    Переменная , ).

    const char* TImagesFactory::psGimpbuttondown_png = 
    (const char*)ucGimpbuttondown_png;
    

    Рассмотрим использование бинарных ресурсов для создания . Как видно из листинга 12.5, реализация класса компонента содержимого практически не изменилась: мы лишь подключили пространство имен TImagesFactory и воспользовались методом static const Image ImageCache::getFromMemory(const void* imageData, int dataSize) для загрузки изображений в DrawableImage не из графического файла, а из памяти.

    #include "TCentralComponent.h"
    // Подключаем пространство имен TImagesFactory
    #include "TImagesFactory.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::textColourId, Colours::black);
      pHelloLabel->setColour(TextEditor::backgroundColourId, Colours::azure);
    
      // Изображение для ненажатой кнопки
      DrawableImage UpImage;
      // Изображение для нажатой кнопки
      DrawableImage DownImage;
      // Загружаем изображение в DrawableImage из памяти
      UpImage.setImage(ImageCache::getFromMemory(
                        TImagesFactory::psGimpbuttonup_png,
                        TImagesFactory::iGimpbuttonup_pngSize));
      DownImage.setImage(ImageCache::getFromMemory(
                       TImagesFactory::psGimpbuttondown_png,
                       TImagesFactory::iGimpbuttondown_pngSize));
    
      // Создаем пустую DrawableButton
      pCloseButton = new DrawableButton("CloseButton", 
                       DrawableButton::ImageAboveTextLabel);
      // Устанавливаем изображения для различных состояний кнопки
      pCloseButton->setImages( UpImage,  UpImage,  DownImage);
      addAndMakeVisible(pCloseButton);
      pCloseButton->setButtonText(tr("Закрыть"));
      pCloseButton->addListener(this);
    
      setSize (400, 200);
    }
    //---------------------------------------------------------------------
    

    Откомпилируем новую версию программы и убедимся, что по своему виду и функциональности она ничем не отличается от предыдущей рис 12.4, но лишена ее недостатков.

    Как уже отмечалось выше, с помощью ряда классов — потомков DrawableDrawableButton может отображать не только готовые изображения, но и различные графические примитивы. Пример создания сложных кнопок DrawableButton в виде звезд с произвольным числом лучей с использованием класса DrawablePath входит в поставку Juce (см. <каталог Juce>/juce/extras/JuceDemo/Source/demos, файл WidgetsDemo.cpp).

    В данном примере DrawableButton легко можно заменить на ShapeButton — кнопку, содержащую залитую геометрическую фигуру (shape). Установить эту фигуру можно с помощью метода void ShapeButton::setShape(const Path newShape, bool resizeNowToFitThisShape, bool maintainShapeProportions, bool hasDropShadow), где newShape — собственно, рисуемая фигура (объект класса Path), а maintainShapeProportions и hasDropShadow — флаги того, должны ли размеры кнопки подстраиваться под размеры фигуры, и того, должна ли фигура отбрасывать тень, соответственно.

    Мы не будем подробно останавливаться на работе с ImageButton, поскольку она сходна с таковой для DrawableButton, разница лишь в использовании объектов класса Image, а не Drawable в методе setImages плюс задание цветов кнопки при манипуляции с ней мышью (наведение и т.п.). Пример использования ImageButton имеется в демонстрационном приложении Juce (см. <каталог Juce>/juce/extras/JuceDemo/Source/demos, файл WidgetsDemo.cpp).

    ToolbarItcodeComponent — кнопка инструментальной панели — подробно рассмотрена в лекции 15 (комоненты панели инструментов).

    HyperlinkButton — кнопка, отображающая подчеркнутую гиперссылку на ресурс Web, который запускается в браузере, установленном по умолчанию, при щелчке по ней. Особенностью кнопки является то, что для ее работы не нужен слушатель (ButtonListener), хотя его установка и возможна, например, если вы хотите, чтобы были выполнены какие-либо действия перед переходом по ссылке.

    Конструктор класса кнопки с гипертекстовой ссылкой HyperlinkButton::HyperlinkButton(const String linkText, const URL linkURL) принимает два параметра: linkText — отображаемый текст и linkURL — собственно гиперссылка, объект специального класса Juce, URL.

    Ссылку на ресурс Web можно изменить с помощью метода void HyperlinkButton::setURL(const URL newURL ) throw().

    Как и у любой кнопки, текст HyperlinkButton можно изменить методом setButtonText.

    Пример использования HyperlinkButton представлен в листинге 12.6, внешний вид работающей программы показан на рисунке12.6

    #include "TCentralComponent.h"
    //---------------------------------------------------------------------
    #define tr(s) String::fromUTF8(s)
    //---------------------------------------------------------------------
    TCentralComponent::TCentralComponent() : Component("Central Component"),
                         pHyperlinkButton(0)
    {
      // Кнопка с гиперссылкой
      pHyperlinkButton = 
      new HyperlinkButton(L"http://www.rawmaterialsoftware.com/juce.php",
                URL(L"http://www.rawmaterialsoftware.com/juce.php"));
    JuceHyperlinkButton  // Устанавливаем текст всплывающей подсказки
      pHyperlinkButton->setTooltip(
                    L"http://www.rawmaterialsoftware.com/juce.php");
      addAndMakeVisible(pHyperlinkButton);
    
      setSize(400, 200);
    }
    //---------------------------------------------------------------------
    TCentralComponent::~TCentralComponent()
    {
      // Удаляем дочерний виджет
      // и обнуляем его указатель
      deleteAndZero(pHyperlinkButton);
    }
    //---------------------------------------------------------------------
    void TCentralComponent::paint(Graphics Canvas)
    {
      Canvas.fillAll(Colours::white);
    }
    //---------------------------------------------------------------------
    void TCentralComponent::resized()
    {
      pHyperlinkButton->setBounds(15, proportionOfHeight(0.4500f), 
                    getWidth() - 15, 25);
    }
    //---------------------------------------------------------------------
    
    (рис 12.6) Внешний вид программы с HyperlinkButton

    Краткие итоги

    В данной лекции нами рассмотрены классы, необходимые для создания кнопок различного вида: кнопок с текстом TextButton, кнопок со стрелкой ArrowButton, кнопок с изображениями (ImageButton, DrawableButton и ShapeButton) и гиперссылки (HyperlinkButton). Базовым классом для всех кнопок является Button.

    Упражнение

    Напишите игру "Светофор": при нажатии на одну из трех кнопок, расположенных вертикально, она окрашивается в соответствующий цвет (красный, желтый или зеленый), а остальные — в темно-серый.

    Дополнительные материалы

    Архив с исходными текстами примеров Вы можете скачать здесь

    Вернуться к учебному плану