Цель лекции: Научиться создавать и использовать главное и контекстное меню в своих программах
Непременным атрибутом современного профессионально выглядящего приложения является меню. Оно представляет собой ограниченную область с набором пунктов, выбирая которые, пользователь вызывает определённые команды приложения. Каждой команде присваивается уникальный идентификационный номер, по которому к ней можно обращаться однозначно.
Основными типами меню являются:
Меню верхнего уровня является результатом совместной работы нескольких классов компонентов: MenuBarComponent, который создаёт полосу под заголовком окна и отвечает за отображение команд, которые (вместе с их обработчиками) создаёт MenuBarModel, а также PopupMenu, объекты которого образуют выпадающие меню и подменю.
Класс PopupMenu создаёт также и контекстное меню.
Конструктор MenuBarComponent в качестве параметра принимает указатель на объект MenuBarModel: MenuBarComponent::MenuBarComponent(MenuBarModel* model). В случае, если мы создаём меню главного окна приложения (а на практике именно так и происходит), то удобнее воспользоваться методом класса DocumentWindow void DocumentWindow::setMenuBar(MenuBarModel* menuBarModel, int menuBarHeight = 0), который также принимает в качестве параметра указатель на объект MenuBarModel, который и формирует внешний вид меню. Второй принимаемый параметр — это ширина меню; в случае, если он принимает значение 0, то ширина полосы меню будет установлена по умолчанию.
Если функция setMenuBar принимает нулевое значение, любое существующее меню будет удалено из главного окна и тогда мы можем добавить объект MenuBarComponent с помощью метода void DocumentWindow::setMenuBarComponent(Component* newMenuBarComponent). Получить текущее меню можно с помощью функции-члена класса DocumentWindow Component* DocumentWindow::getMenuBarComponent() const.
Рассмотрим создание меню верхнего уровня и контекстного меню на примере простого приложения (рис 15.1).
Для начала создадим основу нашего меню — её модель. Пусть наши меню, как верхнего уровня, так и контекстное, включают три команды: "Новый", "Открыть" и "Выход". При этом реально работать (осуществлять выход из программы) будет только последняя; при щелчке по первым двум будет выводиться информационное сообщение.
(рис 15.1) Приложение, демонстрирующее работу меню.
В качестве модели меню будет выступать класс компонента содержимого, который мы унаследуем от класса ).
#ifndef _TCentralComponent_h_
#define _TCentralComponent_h_
//------------------------------------------
#include "../JuceLibraryCode/JuceHeader.h"
//------------------------------------------
// Класс компонента содержимого.
// Наследует класс модели меню
class TCentralComponent : public Component,
public MenuBarModel
{
public:
TCentralComponent();
~TCentralComponent();
// Идентификаторы команд меню
enum TMenuOptions
{
FileNew = 1,
FileOpen = 2,
FileQuit = 3
};
void paint(Graphics);
void resized();
// Функции, наследуемые от MenuBarModel
const StringArray getMenuBarNames();
void menuItemSelected(int, int);
const PopupMenu getMenuForIndex(int, const String);
// Вызывает контекстное меню
void mouseDown(const MouseEvent);
private:
// Ярлык для вывода сообщений
Label* pMessagesLabel;
// Изображения - пиктограммы меню
Image ImageNew_png;
Image ImageOpen_png;
Image ImageExit_png;
// Предотвращает создание копии конструктора и оператора =
TCentralComponent(const TCentralComponent);
const TCentralComponent operator= (const TCentralComponent);
};
//---------------------------------------------
#endif
Класс ), которые должны быть переопределены в классе компонента содержимого.
Метод ).
const StringArray TCentralComponent::getMenuBarNames()
{
// Создаются основные пункты меню
StringArray sMenuNames;
sMenuNames.add(tr("Файл"));
return sMenuNames;
}
Метод virtual void MenuBarModel::menuItemSelected(int menuItemID, int topLevelMenuIndex) вызывается всякий раз, когда пользователь щёлкает по тому или иному пункту меню. Идентификатор команды, вызываемой пользователем, и является первым параметром функции. Второй параметр — идентификатор того меню верхнего уровня, в котором была выбрана команда menuItemID.
Именно в этом методе и находятся обработчики команд меню, выбираемых по их числовым идентификаторам ().
void TCentralComponent::menuItemSelected(int iMenuItemID, int iTopLevelMenuIndex)
{
// Обработчики команд меню
switch(iMenuItemID)
{
// Если выбран пункт "Выход"...
case FileQuit:
{
// Завершаем работу программы
JUCEApplication::quit();
break;
}
// Если выбран пункт "Новый"...
case FileNew:
{
// показываем сообщение
AlertWindow::showMessageBox(AlertWindow::InfoIcon, tr("Информация"),
tr("Выбран пункт \"Новый\""), tr("Да"), 0);
break;
}
// Если выбран пункт "Открыть"...
case FileOpen:
{
// показываем сообщение
AlertWindow::showMessageBox(AlertWindow::InfoIcon, tr("Информация"),
tr("Выбран пункт \"Открыть\""), tr("Да"), 0);
break;
}
}
}
Метод ).
const PopupMenu TCentralComponent::getMenuForIndex(int iIndex,
const String sMenuName)
{
// Создаём выпадающее меню
PopupMenu Menu;
switch(iIndex)
{
// Индекс 0 соответствует пункту меню "Файл"
// (см. getMenuBarNames)
case 0:
{
// Добавляем новые пункты в меню "Файл"
Menu.addItem(FileNew, tr("Новый"), true, false, ImageNew_png);
Menu.addItem(FileOpen, tr("Открыть"), true, false, ImageOpen_png);
// Добавляем разделитель
Menu.addSeparator();
Menu.addItem(FileQuit, tr("Выход"), true, false, ImageExit_png);
break;
}
}
return Menu;
}
Для добавления новых пунктов в выпадающее меню мы воспользовались методом void PopupMenu::addItem(int itemResultId, const String itemText, bool isEnabled = true, bool isTicked = false const Image iconToUse = Image::null) , класса PopupMenu. Метод принимает следующие параметры:
itemResultId — число, возвращаемое методом show класса PopupMenu, когда пользователь выбирает пункт меню; как понятно, в качестве этого параметра передаётся идентификатор соответствующей команды;itemText — текст пункта меню для отображения;isEnabled — если параметр принимает значение true, пункт меню окрашивается в серый цвет и не может быть выбран;isTicked — если параметр принимает значение true, рядом с пунктом меню отображается галочка;iconToUse — если параметр не равен нулю, с левой стороны пункта меню отображается пиктограмма (icon).Для того, чтобы удалить все пункты из меню, необходимо вызвать метод void PopupMenu::clear().
Практически также создаётся контекстное меню. Для его отображения нам необходимо переопределить функцию — обработчик события нажатия пользователем кнопки мыши ().
void TCentralComponent::mouseDown(const MouseEvent Event)
{
// Если пользователь пытается вызвать контекстное меню...
if(Event.mods.isPopupMenu())
{
// создаём его...
PopupMenu LabelPopupMenu;
// и добавляем в него пункты
LabelPopupMenu.addItem(FileNew, tr("Новый"), true, false,
ImageNew_png);
LabelPopupMenu.addItem(FileOpen, tr("Открыть"), true, false,
ImageOpen_png);
LabelPopupMenu.addSeparator();
LabelPopupMenu.addItem(FileQuit, tr("Выход"), true, false,
ImageExit_png);
// Показываем контекстное меню.
const int iResult = LabelPopupMenu.show();
// В зависимости от идентификатора команды
// вызываем тот или иной обработчик.
if(iResult == FileNew)
{
AlertWindow::showMessageBox(AlertWindow::InfoIcon, tr("Информация"),
tr("Выбран пункт \"Новый\""), tr("Да"), 0);
}
else if(iResult == FileOpen)
{
AlertWindow::showMessageBox(AlertWindow::InfoIcon, tr("Информация"),
tr("Выбран пункт \"Открыть\""), tr("Да"), 0);
}
else if(iResult == FileQuit)
{
JUCEApplication::quit();
}
}
}
Поскольку контекстное меню вызывается нажатием правой кнопки мыши, а метод mouseDown вызывается, когда пользователь нажимает любую кнопку, мы предусмотрели соответствующее условие выполнения: мы вызываем публичное свойство класса MouseEvent const ModifierKeys MouseEvent::mods, которое представляет собой ключевой модификатор, ассоциированный с этим событием. Метод bool ModifierKeys::isPopupMenu() const throw() класса ModifierKeys позволяет узнать, было ли это событие связано с вызовом пользователем контекстного меню. Для подобной проверки рекомендуется вызывать именно этот метод, а не bool ModifierKeys::isRightButtonDown() const throw(), информирующий о нажатии правой кнопки мыши, исходя из соображений совместимости с Mac OS X, где контекстное меню вызывается как нажатием на клавишу <CTRL>, так и правой кнопкой мыши.
После этого создаётся контекстное меню, в которое добавляются пункты уже знакомым по меню верхнего уровня способом.
Показ контекстного меню осуществляет уже упоминавшаяся выше функция int PopupMenu::show(int itemIdThatMustBeVisible = 0, int minimumWidth = 0, int maximumNumColumns = 0, int standardItemHeight = 0, ModalComponentManager::Callback * callback = 0). Она принимает следующие параметры:
int itemIdThatMustBeVisible — идентификатор пункта меню, который должен отображаться в нём при первом запуске (быть обязательно видимым). Если меню содержит слишком много пунктов, то оно будет прокручено до этого пункта;int minimumWidth — минимальная ширина меню в пикселях; int maximumNumColumns — число столбцов для упорядочения пунктов меню, если они не помещаются в одном вертикальном. Если параметр принимает значение 0, то будет установлено число столбцов по умолчанию (обычно около 7);int standardItemHeight — если значение параметра не равно нулю, оно используется для задания стандартной высоты элементов меню; ModalComponentManager::Callback * callback — если значение параметра не равно нулю, меню будет запущено асинхронно.В нашем примере мы оставили все параметры принятыми по умолчанию.
В дальнейшем в зависимости от идентификатора команды мы вызываем тот или иной её обработчик ().
Внешний вид программы с вызванным контекстным меню представлен на рисунке 15.2 .
(рис 15.2) Работа программы с вызванным контекстным меню
Рассмотренный выше пример описывает создание простейшего меню. Однако в некоторых случаях требуется управление группой пунктов меню, действующих подобно радиогруппе. В этой ситуации необходим менеджер команд приложения, ). При этом пункт меню, соответствующий текущему языку, отмечается галочкой.
(рис 15.3) Работа программы, демонстрирующей совместное использование компонентов меню и ApplicationCommandManager
В качестве основы для нового примера возьмём ранее написанный проект программы, иллюстрирующей использование меню.
Объект класса ApplicationCommandManager хранит список команд, которые может выполнять программа, и вызывает их по мере необходимости. С помощью ApplicationCommandManager можно организовать совместное реагирование на общие команды меню, кнопок панели инструментов и других компонентов управления, подобно действиям (класс QAction) библиотеки Qt.
Объявим объект класса
#ifndef _TMainForm_h_
#define _TMainForm_h_
//--------------------------------------------------
#include "../JuceLibraryCode/JuceHeader.h"
//--------------------------------------------------
class TCentralComponent;
//--------------------------------------------------
class TMainForm : public DocumentWindow
{
public:
TMainForm();
~TMainForm();
void closeButtonPressed();
// Менеджер команд приложения
ApplicationCommandManager CommandManager;
private:
// Компонент содержимого
TCentralComponent* pCentralComponent;
};
//----------------------------------------
#endif
Приёмником для команд, посылаемых ).
#include "TMainForm.h"
//-------------------------------------------------
#include "TCentralComponent.h"
//-------------------------------------------------
TMainForm::TMainForm() : DocumentWindow(
JUCEApplication::getInstance()->getApplicationName(),
Colours::azure,
DocumentWindow::allButtons)
{
centreWithSize(700, 400);
setResizable(true, false);
setVisible(true);
// Динамически создаём экземпляр класса нашего компонента
pCentralComponent = new TCentralComponent(this);
// Устанавливаем его в качестве компонента содержимого
setContentOwned(pCentralComponent, true);
// Регистрируем все созданные команды компонента содержимого
CommandManager.registerAllCommandsForTarget(pCentralComponent);
// Регистрируем все текущие команды приложения
CommandManager.registerAllCommandsForTarget(JUCEApplication::getInstance());
setMenuBar(pCentralComponent);
}
//-------------------------------------------------
TMainForm::~TMainForm()
{
setMenuBar(NULL);
}
//-------------------------------------------------
void TMainForm::closeButtonPressed()
{
JUCEApplication::getInstance()->systemRequestedQuit();
}
//-------------------------------------------------
Класс
Virtual ApplicationCommandTarget* ApplicationCommandTarget::getNextCommandTarget() - возвращает следующий после текущего приёмник команд;virtual void ApplicationCommandTarget::getAllCommands(Array< CommandID > commands) — возвращает список всех команд, которые могут быть обработаны;virtual void ApplicationCommandTarget::(CommandID commandID, ApplicationCommandInfo result) — предоставляет всю необходимую информацию по обрабатываемой команде; не считая идентификатора команды, которым является первый параметр метода, эта информация содержится в объекте ApplicationCommandInfo, который предварительно должен быть создан и инициализирован;virtual bool ApplicationCommandTarget::perform(const InvocationInfo info) — запускает команду на выполнение, получая всю необходимую информацию о ней из объекта InvocationInfo, конструктор которого в качестве параметра принимает идентификатор команды. Если все обработчики команд меню находятся в этой функции, то ранее рассмотренный метод menuItemSelected следует оставить пустым.#ifndef _TCentralComponent_h_
#define _TCentralComponent_h_
//------------------------------------------------
#include "../JuceLibraryCode/JuceHeader.h"
#include "TMainForm.h"
//------------------------------------------------
// Класс компонента содержимого.
// Наследует класс приёмника команд
class TCentralComponent : public Component,
public MenuBarModel,
public ApplicationCommandTarget
{
public:
TCentralComponent(TMainForm* pParent);
~TCentralComponent();
// Идентификаторы команд меню
enum TMenuOptions
{
FileNew = 1,
FileOpen = 2,
FileQuit = 3,
LanguageEnglish = 4,
LanguageRussian = 5
};
void paint(Graphics);
void resized();
void mouseDown(const MouseEvent);
const StringArray getMenuBarNames();
void menuItemSelected(int, int);
const PopupMenu getMenuForIndex(int, const String);
// Функции, наследуемые от ApplicationCommandTarget
// Возвращает следующий после текущего приёмник команд
ApplicationCommandTarget* getNextCommandTarget();
// Возвращает список всех команд, которые могут быть обработаны
void getAllCommands(Array<int>);
// Предоставляет всю необходимую информацию по обрабатываемой команде
void getCommandInfo(CommandID, ApplicationCommandInfo);
// Запускает команду на выполнение
bool perform(const InvocationInfo);
// Обработчик смены команд интерфейса
void LanguageChange();
private:
// Флаги, какой язык выбран
bool bEnglish;
bool bRussian;
// Родительский компонент
TMainForm* pParent;
// Ярлык для вывода сообщений
Label* pMessagesLabel;
// Строки интерфейса
String sMessagesLabelText;
String sFileMenuText;
String sLanguageMenuText;
String sFileNewMenuText;
String sFileOpenMenuText;
String sFileQuitText;
String sMessageTitleText;
String sMessageNewText;
String sMessageOpenText;
String sYesButtonText;
Image ImageNew_png;
Image ImageOpen_png;
Image ImageExit_png;
// Предотвращает создание копии конструктора и оператора =
TCentralComponent(const TCentralComponent);
const TCentralComponent operator= (const TCentralComponent);
};
//---------------------------------------------------
#endif
Как видно из листинга 15.8 , в нашем новом приложении в список идентификаторов команд меню мы добавили две новые: LanguageEnglish и LanguageRussian. Именно эти команды будут обрабатываться с помощью методов ApplicationCommanTarget, а остальные — ранее описанным способом.
Кроме того, мы предусмотрели метод ).
Следует понимать, что подобный способ перевода интерфейса, хотя и работает, но приведён исключительно как пример использования командного менеджера. Локализацию приложений, написанных на основе Juce, удобнее осуществлять с помощью класса LocalisedStrings, описание которого дано в лекции 22 .
Основным отличием реализации конструктора нашего приложение является то, что мы запоминаем указатель на окно главного приложения. Это необходимо для того, чтобы иметь возможность доступа к его командному менеджеру (, ).
#include "TCentralComponent.h"
#include"TImagesFactory.h"
//-------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//-------------------------------------------------------
TCentralComponent::TCentralComponent(TMainForm* pParent)
: Component("Central Component"),
pMessagesLabel(0)
{
this->pParent = pParent;
pMessagesLabel = new Label(L"Messages Label", "");
pMessagesLabel->setFont(Font(15.0000f, Font::plain));
pMessagesLabel->setJustificationType(Justification::centred);
pMessagesLabel->setEditable (false, false, false);
pMessagesLabel->setColour(Label::outlineColourId, Colours::black);
pMessagesLabel->setColour(Label::textColourId, Colours::black);
pMessagesLabel->setColour(Label::backgroundColourId, Colours::azure);
addAndMakeVisible(pMessagesLabel);
ImageExit_png = ImageCache::getFromMemory(TImagesFactory::psExit_png,
TImagesFactory::iExit_pngSize);
ImageNew_png = ImageCache::getFromMemory(TImagesFactory::psNew_png,
TImagesFactory::iNew_pngSize);
ImageOpen_png = ImageCache::getFromMemory(TImagesFactory::psOpen_png,
TImagesFactory::iOpen_pngSize);
setSize (700, 400);
bEnglish = true;
bRussian = false;
LanguageChange();
}
//-----------------------------------------------------
TCentralComponent::~TCentralComponent()
{
// Удаляем дочерние виджеты
// и обнуляем их указатели
deleteAllChildren();
}
//-----------------------------------------------------
void TCentralComponent::LanguageChange()
{
if(bEnglish)
{
sMessagesLabelText = tr(
quot;Choose an option of the main menu \nor right-click outside of the label
to invoke a context menu.");
sFileMenuText = tr("File");
sLanguageMenuText = tr("Language");
sFileNewMenuText = tr("New");
sFileOpenMenuText = tr("Open");
sFileQuitText = tr("Quit");
sMessageTitleText = tr("Information");
sMessageNewText = tr("Option \"New\" was selected");
sMessageOpenText = tr("Option \"Open\" was selected");
sYesButtonText = tr("Yes");
}
else if(bRussian)
{
sMessagesLabelText = tr(
"Выберите пункт главного меню \nили щёлкните правой кнопкой мыши
за пределами надписи для того,
чтобы вызвать контекстное меню.");
sFileMenuText = tr("Файл");
sLanguageMenuText = tr("Язык");
sFileNewMenuText = tr("Новый");
sFileOpenMenuText = tr("Открыть");
sFileQuitText = tr("Выход");
sMessageTitleText = tr("Информация");
sMessageNewText = tr("Выбран пункт \"Новый\"");
sMessageOpenText = tr("Выбран пункт \"Открыть\"");
sYesButtonText = tr("Да");
}
pMessagesLabel->setText(sMessagesLabelText, false);
getMenuBarNames();
// Перерисовываем пункты меню с учётом нового языка
menuItemsChanged();
}
//--------------------------------------------------
В конструкторе компонента содержимого мы вызываем наш метод LanguageChange для задания строк интерфейса приложения. Также эта функция будет вызываться всякий раз, когда пользователь выбирает новый пункт в меню "Язык". Флагом выбора того или иного языка выступают булевые переменные bEnglish и bRussian. Для того, чтобы принудительно перерисовать пункты меню, исходя из новых языковых настроек, мы вызываем метод void MenuBarModel::menuItemsChanged().
Рассмотрим реализацию функций, унаследованных от класса ).
ApplicationCommandTarget* TCentralComponent::getNextCommandTarget()
{getAllCommands
return findFirstTargetParentComponent();
}
void TCentralComponent::getAllCommands(Array<int> iCommands)
{
// Регистрация команд для командного менеджера
const CommandID IDs[] =
{
LanguageEnglish,
LanguageRussian
};
iCommands.addArray(IDs, numElementsInArray(IDs));
}
Метод ).
void TCentralComponent::getCommandInfo(CommandID ID, ApplicationCommandInfo
Result)
{
switch(ID)
{
case LanguageEnglish:
{
Result.setInfo(L"English", L"Switch to English", "Language", 0);
Result.setTicked(bEnglish);
break;
}
case LanguageRussian:
{
Result.setInfo(tr("Русский"), L"Switch to Russian", "Language", 0);
Result.setTicked(bRussian);
break;
}
}
}
Как мы уже упоминали, за это отвечает объект класса ApplicationCommandInfo. Отображение соответствующих команде пунктов меню в программе мы задаём вызовом метода void ApplicationCommandInfo::setInfo(const String shortName, const String description, const String categoryName, int flags), который принимает следующие параметры:
shortName — краткое название пункта меню;description — его описание;categoryName — категория; flags — флаги.Флагами являются идентификаторы перечислимого типа enum ApplicationCommandInfo::CommandFlags:
isDisabled — пункт меню недоступен для выбора пользователем;isTicked — рядом с пунктом стоит галочка;wantsKeyUpDownCallbacks — команда вызывается дважды;hiddenFromKeyEditor — делает команду недоступной для включения в список команд объекта класса KeyMappingEditorComponent (хранит карту клавиатурных комбинаций);readOnlyInKeyEditor — делает команду доступной для включения в список команд объекта класса KeyMappingEditorComponent (хранит карту клавиатурных комбинаций), но не позволяет менять ассоциированную клавиатурную комбинацию;dontTriggerVisualFeedback — если команда была вызвана с помощью клавиатурной комбинации, пункты меню на это не реагируют.В нашем примере параметр ). Аналогично функция void ApplicationCommandInfo::setActive(bool isActive) throw() может делать пункт меню активным или неактивным, устанавливая или сбрасывая флаг isDisabled.
И наконец, рассмотрим метод ). Их работа понятна из комментариев.
bool TCentralComponent::perform(const InvocationInfo Info)
{
switch(Info.commandID)
{
case LanguageEnglish:
{
// Отмечаем пункт меню - английский
bEnglish = true;
bRussian = false;
break;
}
case LanguageRussian:
{
// Отмечаем пункт меню - русский
bEnglish = false;
bRussian = true;
break;
}
}
// Меняем язык интерфейса
LanguageChange();
return true;
}
Для создания меню верхнего уровня вашего приложения необходимо описать набор команд и связать его с соответствующими в модели меню (MenuBarModel). За отображение пунктов меню и выпадающих меню отвечает класс MenuBarComponent. Как выпадающее, так и контекстное меню создаётся объектами класса PopupMenu.
Архив с исходными текстами примеровВы можете скачать здесь
Цель лекции: Научиться создавать и использовать главное и контекстное меню в своих программах
Непременным атрибутом современного профессионально выглядящего приложения является меню. Оно представляет собой ограниченную область с набором пунктов, выбирая которые, пользователь вызывает определённые команды приложения. Каждой команде присваивается уникальный идентификационный номер, по которому к ней можно обращаться однозначно.
Основными типами меню являются:
Меню верхнего уровня является результатом совместной работы нескольких классов компонентов: MenuBarComponent, который создаёт полосу под заголовком окна и отвечает за отображение команд, которые (вместе с их обработчиками) создаёт MenuBarModel, а также PopupMenu, объекты которого образуют выпадающие меню и подменю.
Класс PopupMenu создаёт также и контекстное меню.
Конструктор MenuBarComponent в качестве параметра принимает указатель на объект MenuBarModel: MenuBarComponent::MenuBarComponent(MenuBarModel* model). В случае, если мы создаём меню главного окна приложения (а на практике именно так и происходит), то удобнее воспользоваться методом класса DocumentWindow void DocumentWindow::setMenuBar(MenuBarModel* menuBarModel, int menuBarHeight = 0), который также принимает в качестве параметра указатель на объект MenuBarModel, который и формирует внешний вид меню. Второй принимаемый параметр — это ширина меню; в случае, если он принимает значение 0, то ширина полосы меню будет установлена по умолчанию.
Если функция setMenuBar принимает нулевое значение, любое существующее меню будет удалено из главного окна и тогда мы можем добавить объект MenuBarComponent с помощью метода void DocumentWindow::setMenuBarComponent(Component* newMenuBarComponent). Получить текущее меню можно с помощью функции-члена класса DocumentWindow Component* DocumentWindow::getMenuBarComponent() const.
Рассмотрим создание меню верхнего уровня и контекстного меню на примере простого приложения (рис 15.1).
Для начала создадим основу нашего меню — её модель. Пусть наши меню, как верхнего уровня, так и контекстное, включают три команды: "Новый", "Открыть" и "Выход". При этом реально работать (осуществлять выход из программы) будет только последняя; при щелчке по первым двум будет выводиться информационное сообщение.
(рис 15.1) Приложение, демонстрирующее работу меню.
В качестве модели меню будет выступать класс компонента содержимого, который мы унаследуем от класса ).
#ifndef _TCentralComponent_h_
#define _TCentralComponent_h_
//------------------------------------------
#include "../JuceLibraryCode/JuceHeader.h"
//------------------------------------------
// Класс компонента содержимого.
// Наследует класс модели меню
class TCentralComponent : public Component,
public MenuBarModel
{
public:
TCentralComponent();
~TCentralComponent();
// Идентификаторы команд меню
enum TMenuOptions
{
FileNew = 1,
FileOpen = 2,
FileQuit = 3
};
void paint(Graphics);
void resized();
// Функции, наследуемые от MenuBarModel
const StringArray getMenuBarNames();
void menuItemSelected(int, int);
const PopupMenu getMenuForIndex(int, const String);
// Вызывает контекстное меню
void mouseDown(const MouseEvent);
private:
// Ярлык для вывода сообщений
Label* pMessagesLabel;
// Изображения - пиктограммы меню
Image ImageNew_png;
Image ImageOpen_png;
Image ImageExit_png;
// Предотвращает создание копии конструктора и оператора =
TCentralComponent(const TCentralComponent);
const TCentralComponent operator= (const TCentralComponent);
};
//---------------------------------------------
#endif
Класс ), которые должны быть переопределены в классе компонента содержимого.
Метод ).
const StringArray TCentralComponent::getMenuBarNames()
{
// Создаются основные пункты меню
StringArray sMenuNames;
sMenuNames.add(tr("Файл"));
return sMenuNames;
}
Метод virtual void MenuBarModel::menuItemSelected(int menuItemID, int topLevelMenuIndex) вызывается всякий раз, когда пользователь щёлкает по тому или иному пункту меню. Идентификатор команды, вызываемой пользователем, и является первым параметром функции. Второй параметр — идентификатор того меню верхнего уровня, в котором была выбрана команда menuItemID.
Именно в этом методе и находятся обработчики команд меню, выбираемых по их числовым идентификаторам ().
void TCentralComponent::menuItemSelected(int iMenuItemID, int iTopLevelMenuIndex)
{
// Обработчики команд меню
switch(iMenuItemID)
{
// Если выбран пункт "Выход"...
case FileQuit:
{
// Завершаем работу программы
JUCEApplication::quit();
break;
}
// Если выбран пункт "Новый"...
case FileNew:
{
// показываем сообщение
AlertWindow::showMessageBox(AlertWindow::InfoIcon, tr("Информация"),
tr("Выбран пункт \"Новый\""), tr("Да"), 0);
break;
}
// Если выбран пункт "Открыть"...
case FileOpen:
{
// показываем сообщение
AlertWindow::showMessageBox(AlertWindow::InfoIcon, tr("Информация"),
tr("Выбран пункт \"Открыть\""), tr("Да"), 0);
break;
}
}
}
Метод ).
const PopupMenu TCentralComponent::getMenuForIndex(int iIndex,
const String sMenuName)
{
// Создаём выпадающее меню
PopupMenu Menu;
switch(iIndex)
{
// Индекс 0 соответствует пункту меню "Файл"
// (см. getMenuBarNames)
case 0:
{
// Добавляем новые пункты в меню "Файл"
Menu.addItem(FileNew, tr("Новый"), true, false, ImageNew_png);
Menu.addItem(FileOpen, tr("Открыть"), true, false, ImageOpen_png);
// Добавляем разделитель
Menu.addSeparator();
Menu.addItem(FileQuit, tr("Выход"), true, false, ImageExit_png);
break;
}
}
return Menu;
}
Для добавления новых пунктов в выпадающее меню мы воспользовались методом void PopupMenu::addItem(int itemResultId, const String itemText, bool isEnabled = true, bool isTicked = false const Image iconToUse = Image::null) , класса PopupMenu. Метод принимает следующие параметры:
itemResultId — число, возвращаемое методом show класса PopupMenu, когда пользователь выбирает пункт меню; как понятно, в качестве этого параметра передаётся идентификатор соответствующей команды;itemText — текст пункта меню для отображения;isEnabled — если параметр принимает значение true, пункт меню окрашивается в серый цвет и не может быть выбран;isTicked — если параметр принимает значение true, рядом с пунктом меню отображается галочка;iconToUse — если параметр не равен нулю, с левой стороны пункта меню отображается пиктограмма (icon).Для того, чтобы удалить все пункты из меню, необходимо вызвать метод void PopupMenu::clear().
Практически также создаётся контекстное меню. Для его отображения нам необходимо переопределить функцию — обработчик события нажатия пользователем кнопки мыши ().
void TCentralComponent::mouseDown(const MouseEvent Event)
{
// Если пользователь пытается вызвать контекстное меню...
if(Event.mods.isPopupMenu())
{
// создаём его...
PopupMenu LabelPopupMenu;
// и добавляем в него пункты
LabelPopupMenu.addItem(FileNew, tr("Новый"), true, false,
ImageNew_png);
LabelPopupMenu.addItem(FileOpen, tr("Открыть"), true, false,
ImageOpen_png);
LabelPopupMenu.addSeparator();
LabelPopupMenu.addItem(FileQuit, tr("Выход"), true, false,
ImageExit_png);
// Показываем контекстное меню.
const int iResult = LabelPopupMenu.show();
// В зависимости от идентификатора команды
// вызываем тот или иной обработчик.
if(iResult == FileNew)
{
AlertWindow::showMessageBox(AlertWindow::InfoIcon, tr("Информация"),
tr("Выбран пункт \"Новый\""), tr("Да"), 0);
}
else if(iResult == FileOpen)
{
AlertWindow::showMessageBox(AlertWindow::InfoIcon, tr("Информация"),
tr("Выбран пункт \"Открыть\""), tr("Да"), 0);
}
else if(iResult == FileQuit)
{
JUCEApplication::quit();
}
}
}
Поскольку контекстное меню вызывается нажатием правой кнопки мыши, а метод mouseDown вызывается, когда пользователь нажимает любую кнопку, мы предусмотрели соответствующее условие выполнения: мы вызываем публичное свойство класса MouseEvent const ModifierKeys MouseEvent::mods, которое представляет собой ключевой модификатор, ассоциированный с этим событием. Метод bool ModifierKeys::isPopupMenu() const throw() класса ModifierKeys позволяет узнать, было ли это событие связано с вызовом пользователем контекстного меню. Для подобной проверки рекомендуется вызывать именно этот метод, а не bool ModifierKeys::isRightButtonDown() const throw(), информирующий о нажатии правой кнопки мыши, исходя из соображений совместимости с Mac OS X, где контекстное меню вызывается как нажатием на клавишу <CTRL>, так и правой кнопкой мыши.
После этого создаётся контекстное меню, в которое добавляются пункты уже знакомым по меню верхнего уровня способом.
Показ контекстного меню осуществляет уже упоминавшаяся выше функция int PopupMenu::show(int itemIdThatMustBeVisible = 0, int minimumWidth = 0, int maximumNumColumns = 0, int standardItemHeight = 0, ModalComponentManager::Callback * callback = 0). Она принимает следующие параметры:
int itemIdThatMustBeVisible — идентификатор пункта меню, который должен отображаться в нём при первом запуске (быть обязательно видимым). Если меню содержит слишком много пунктов, то оно будет прокручено до этого пункта;int minimumWidth — минимальная ширина меню в пикселях; int maximumNumColumns — число столбцов для упорядочения пунктов меню, если они не помещаются в одном вертикальном. Если параметр принимает значение 0, то будет установлено число столбцов по умолчанию (обычно около 7);int standardItemHeight — если значение параметра не равно нулю, оно используется для задания стандартной высоты элементов меню; ModalComponentManager::Callback * callback — если значение параметра не равно нулю, меню будет запущено асинхронно.В нашем примере мы оставили все параметры принятыми по умолчанию.
В дальнейшем в зависимости от идентификатора команды мы вызываем тот или иной её обработчик ().
Внешний вид программы с вызванным контекстным меню представлен на рисунке 15.2 .
(рис 15.2) Работа программы с вызванным контекстным меню
Рассмотренный выше пример описывает создание простейшего меню. Однако в некоторых случаях требуется управление группой пунктов меню, действующих подобно радиогруппе. В этой ситуации необходим менеджер команд приложения, ). При этом пункт меню, соответствующий текущему языку, отмечается галочкой.
(рис 15.3) Работа программы, демонстрирующей совместное использование компонентов меню и ApplicationCommandManager
В качестве основы для нового примера возьмём ранее написанный проект программы, иллюстрирующей использование меню.
Объект класса ApplicationCommandManager хранит список команд, которые может выполнять программа, и вызывает их по мере необходимости. С помощью ApplicationCommandManager можно организовать совместное реагирование на общие команды меню, кнопок панели инструментов и других компонентов управления, подобно действиям (класс QAction) библиотеки Qt.
Объявим объект класса
#ifndef _TMainForm_h_
#define _TMainForm_h_
//--------------------------------------------------
#include "../JuceLibraryCode/JuceHeader.h"
//--------------------------------------------------
class TCentralComponent;
//--------------------------------------------------
class TMainForm : public DocumentWindow
{
public:
TMainForm();
~TMainForm();
void closeButtonPressed();
// Менеджер команд приложения
ApplicationCommandManager CommandManager;
private:
// Компонент содержимого
TCentralComponent* pCentralComponent;
};
//----------------------------------------
#endif
Приёмником для команд, посылаемых ).
#include "TMainForm.h"
//-------------------------------------------------
#include "TCentralComponent.h"
//-------------------------------------------------
TMainForm::TMainForm() : DocumentWindow(
JUCEApplication::getInstance()->getApplicationName(),
Colours::azure,
DocumentWindow::allButtons)
{
centreWithSize(700, 400);
setResizable(true, false);
setVisible(true);
// Динамически создаём экземпляр класса нашего компонента
pCentralComponent = new TCentralComponent(this);
// Устанавливаем его в качестве компонента содержимого
setContentOwned(pCentralComponent, true);
// Регистрируем все созданные команды компонента содержимого
CommandManager.registerAllCommandsForTarget(pCentralComponent);
// Регистрируем все текущие команды приложения
CommandManager.registerAllCommandsForTarget(JUCEApplication::getInstance());
setMenuBar(pCentralComponent);
}
//-------------------------------------------------
TMainForm::~TMainForm()
{
setMenuBar(NULL);
}
//-------------------------------------------------
void TMainForm::closeButtonPressed()
{
JUCEApplication::getInstance()->systemRequestedQuit();
}
//-------------------------------------------------
Класс
Virtual ApplicationCommandTarget* ApplicationCommandTarget::getNextCommandTarget() - возвращает следующий после текущего приёмник команд;virtual void ApplicationCommandTarget::getAllCommands(Array< CommandID > commands) — возвращает список всех команд, которые могут быть обработаны;virtual void ApplicationCommandTarget::(CommandID commandID, ApplicationCommandInfo result) — предоставляет всю необходимую информацию по обрабатываемой команде; не считая идентификатора команды, которым является первый параметр метода, эта информация содержится в объекте ApplicationCommandInfo, который предварительно должен быть создан и инициализирован;virtual bool ApplicationCommandTarget::perform(const InvocationInfo info) — запускает команду на выполнение, получая всю необходимую информацию о ней из объекта InvocationInfo, конструктор которого в качестве параметра принимает идентификатор команды. Если все обработчики команд меню находятся в этой функции, то ранее рассмотренный метод menuItemSelected следует оставить пустым.#ifndef _TCentralComponent_h_
#define _TCentralComponent_h_
//------------------------------------------------
#include "../JuceLibraryCode/JuceHeader.h"
#include "TMainForm.h"
//------------------------------------------------
// Класс компонента содержимого.
// Наследует класс приёмника команд
class TCentralComponent : public Component,
public MenuBarModel,
public ApplicationCommandTarget
{
public:
TCentralComponent(TMainForm* pParent);
~TCentralComponent();
// Идентификаторы команд меню
enum TMenuOptions
{
FileNew = 1,
FileOpen = 2,
FileQuit = 3,
LanguageEnglish = 4,
LanguageRussian = 5
};
void paint(Graphics);
void resized();
void mouseDown(const MouseEvent);
const StringArray getMenuBarNames();
void menuItemSelected(int, int);
const PopupMenu getMenuForIndex(int, const String);
// Функции, наследуемые от ApplicationCommandTarget
// Возвращает следующий после текущего приёмник команд
ApplicationCommandTarget* getNextCommandTarget();
// Возвращает список всех команд, которые могут быть обработаны
void getAllCommands(Array<int>);
// Предоставляет всю необходимую информацию по обрабатываемой команде
void getCommandInfo(CommandID, ApplicationCommandInfo);
// Запускает команду на выполнение
bool perform(const InvocationInfo);
// Обработчик смены команд интерфейса
void LanguageChange();
private:
// Флаги, какой язык выбран
bool bEnglish;
bool bRussian;
// Родительский компонент
TMainForm* pParent;
// Ярлык для вывода сообщений
Label* pMessagesLabel;
// Строки интерфейса
String sMessagesLabelText;
String sFileMenuText;
String sLanguageMenuText;
String sFileNewMenuText;
String sFileOpenMenuText;
String sFileQuitText;
String sMessageTitleText;
String sMessageNewText;
String sMessageOpenText;
String sYesButtonText;
Image ImageNew_png;
Image ImageOpen_png;
Image ImageExit_png;
// Предотвращает создание копии конструктора и оператора =
TCentralComponent(const TCentralComponent);
const TCentralComponent operator= (const TCentralComponent);
};
//---------------------------------------------------
#endif
Как видно из листинга 15.8 , в нашем новом приложении в список идентификаторов команд меню мы добавили две новые: LanguageEnglish и LanguageRussian. Именно эти команды будут обрабатываться с помощью методов ApplicationCommanTarget, а остальные — ранее описанным способом.
Кроме того, мы предусмотрели метод ).
Следует понимать, что подобный способ перевода интерфейса, хотя и работает, но приведён исключительно как пример использования командного менеджера. Локализацию приложений, написанных на основе Juce, удобнее осуществлять с помощью класса LocalisedStrings, описание которого дано в лекции 22 .
Основным отличием реализации конструктора нашего приложение является то, что мы запоминаем указатель на окно главного приложения. Это необходимо для того, чтобы иметь возможность доступа к его командному менеджеру (, ).
#include "TCentralComponent.h"
#include"TImagesFactory.h"
//-------------------------------------------------------
#define tr(s) String::fromUTF8(s)
//-------------------------------------------------------
TCentralComponent::TCentralComponent(TMainForm* pParent)
: Component("Central Component"),
pMessagesLabel(0)
{
this->pParent = pParent;
pMessagesLabel = new Label(L"Messages Label", "");
pMessagesLabel->setFont(Font(15.0000f, Font::plain));
pMessagesLabel->setJustificationType(Justification::centred);
pMessagesLabel->setEditable (false, false, false);
pMessagesLabel->setColour(Label::outlineColourId, Colours::black);
pMessagesLabel->setColour(Label::textColourId, Colours::black);
pMessagesLabel->setColour(Label::backgroundColourId, Colours::azure);
addAndMakeVisible(pMessagesLabel);
ImageExit_png = ImageCache::getFromMemory(TImagesFactory::psExit_png,
TImagesFactory::iExit_pngSize);
ImageNew_png = ImageCache::getFromMemory(TImagesFactory::psNew_png,
TImagesFactory::iNew_pngSize);
ImageOpen_png = ImageCache::getFromMemory(TImagesFactory::psOpen_png,
TImagesFactory::iOpen_pngSize);
setSize (700, 400);
bEnglish = true;
bRussian = false;
LanguageChange();
}
//-----------------------------------------------------
TCentralComponent::~TCentralComponent()
{
// Удаляем дочерние виджеты
// и обнуляем их указатели
deleteAllChildren();
}
//-----------------------------------------------------
void TCentralComponent::LanguageChange()
{
if(bEnglish)
{
sMessagesLabelText = tr(
quot;Choose an option of the main menu \nor right-click outside of the label
to invoke a context menu.");
sFileMenuText = tr("File");
sLanguageMenuText = tr("Language");
sFileNewMenuText = tr("New");
sFileOpenMenuText = tr("Open");
sFileQuitText = tr("Quit");
sMessageTitleText = tr("Information");
sMessageNewText = tr("Option \"New\" was selected");
sMessageOpenText = tr("Option \"Open\" was selected");
sYesButtonText = tr("Yes");
}
else if(bRussian)
{
sMessagesLabelText = tr(
"Выберите пункт главного меню \nили щёлкните правой кнопкой мыши
за пределами надписи для того,
чтобы вызвать контекстное меню.");
sFileMenuText = tr("Файл");
sLanguageMenuText = tr("Язык");
sFileNewMenuText = tr("Новый");
sFileOpenMenuText = tr("Открыть");
sFileQuitText = tr("Выход");
sMessageTitleText = tr("Информация");
sMessageNewText = tr("Выбран пункт \"Новый\"");
sMessageOpenText = tr("Выбран пункт \"Открыть\"");
sYesButtonText = tr("Да");
}
pMessagesLabel->setText(sMessagesLabelText, false);
getMenuBarNames();
// Перерисовываем пункты меню с учётом нового языка
menuItemsChanged();
}
//--------------------------------------------------
В конструкторе компонента содержимого мы вызываем наш метод LanguageChange для задания строк интерфейса приложения. Также эта функция будет вызываться всякий раз, когда пользователь выбирает новый пункт в меню "Язык". Флагом выбора того или иного языка выступают булевые переменные bEnglish и bRussian. Для того, чтобы принудительно перерисовать пункты меню, исходя из новых языковых настроек, мы вызываем метод void MenuBarModel::menuItemsChanged().
Рассмотрим реализацию функций, унаследованных от класса ).
ApplicationCommandTarget* TCentralComponent::getNextCommandTarget()
{getAllCommands
return findFirstTargetParentComponent();
}
void TCentralComponent::getAllCommands(Array<int> iCommands)
{
// Регистрация команд для командного менеджера
const CommandID IDs[] =
{
LanguageEnglish,
LanguageRussian
};
iCommands.addArray(IDs, numElementsInArray(IDs));
}
Метод ).
void TCentralComponent::getCommandInfo(CommandID ID, ApplicationCommandInfo
Result)
{
switch(ID)
{
case LanguageEnglish:
{
Result.setInfo(L"English", L"Switch to English", "Language", 0);
Result.setTicked(bEnglish);
break;
}
case LanguageRussian:
{
Result.setInfo(tr("Русский"), L"Switch to Russian", "Language", 0);
Result.setTicked(bRussian);
break;
}
}
}
Как мы уже упоминали, за это отвечает объект класса ApplicationCommandInfo. Отображение соответствующих команде пунктов меню в программе мы задаём вызовом метода void ApplicationCommandInfo::setInfo(const String shortName, const String description, const String categoryName, int flags), который принимает следующие параметры:
shortName — краткое название пункта меню;description — его описание;categoryName — категория; flags — флаги.Флагами являются идентификаторы перечислимого типа enum ApplicationCommandInfo::CommandFlags:
isDisabled — пункт меню недоступен для выбора пользователем;isTicked — рядом с пунктом стоит галочка;wantsKeyUpDownCallbacks — команда вызывается дважды;hiddenFromKeyEditor — делает команду недоступной для включения в список команд объекта класса KeyMappingEditorComponent (хранит карту клавиатурных комбинаций);readOnlyInKeyEditor — делает команду доступной для включения в список команд объекта класса KeyMappingEditorComponent (хранит карту клавиатурных комбинаций), но не позволяет менять ассоциированную клавиатурную комбинацию;dontTriggerVisualFeedback — если команда была вызвана с помощью клавиатурной комбинации, пункты меню на это не реагируют.В нашем примере параметр ). Аналогично функция void ApplicationCommandInfo::setActive(bool isActive) throw() может делать пункт меню активным или неактивным, устанавливая или сбрасывая флаг isDisabled.
И наконец, рассмотрим метод ). Их работа понятна из комментариев.
bool TCentralComponent::perform(const InvocationInfo Info)
{
switch(Info.commandID)
{
case LanguageEnglish:
{
// Отмечаем пункт меню - английский
bEnglish = true;
bRussian = false;
break;
}
case LanguageRussian:
{
// Отмечаем пункт меню - русский
bEnglish = false;
bRussian = true;
break;
}
}
// Меняем язык интерфейса
LanguageChange();
return true;
}
Для создания меню верхнего уровня вашего приложения необходимо описать набор команд и связать его с соответствующими в модели меню (MenuBarModel). За отображение пунктов меню и выпадающих меню отвечает класс MenuBarComponent. Как выпадающее, так и контекстное меню создаётся объектами класса PopupMenu.
Архив с исходными текстами примеровВы можете скачать здесь
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.