Графические интерфейсы X: Текстовое поле ввода, слайдер картинок и простые элементы управления (build 5)
Содержание
- Введение
- Элемент «Текстовое поле ввода»
- Класс для создания элемента «Текстовое поле ввода»
- Элемент «Слайдер картинок»
- Класс для создания элемента «Слайдер картинок»
- Элементы «Текстовая метка» и «Картинка»
- Класс CFonts для работы со шрифтами
- Список дополнительных обновлений библиотеки
- Приложение для теста элемента
- Заключение
Введение
О том, для чего предназначена эта библиотека, более подробно можно прочитать в первой статье серии: Графические интерфейсы I: Подготовка структуры библиотеки (Глава 1). В конце статей каждой части представлен список глав со ссылками, и там же есть возможность загрузить к себе на компьютер полную версию библиотеки на текущей стадии разработки. Файлы нужно разместить по тем же директориям, как они расположены в архиве.
В этой статье рассмотрим новые элементы управления: «Текстовое поле ввода», «Слайдер картинок», а также простые дополнительные элементы: «Текстовая метка» и «Картинка», которые тоже могут пригодиться в различных случаях. Библиотека продолжает развиваться, и кроме добавления новых элементов управления, улучшаются и те, которые уже были созданы ранее. Поскольку библиотеку применяют в своей работе уже множество пользователей, то от них поступают различные замечания и предложения. Многие из этих просьб реализованы в новой версии библиотеки. Плюс ко всему были оптимизированы некоторые алгоритмы. Это ещё сильнее уменьшило потребление ресурсов процессора. Подробнее обо всём этом читайте далее в статье.
Элемент «Текстовое поле ввода»
До сих пор в разрабатываемой библиотеке уже присутствовал элемент «Поле ввода» (класс CSpinEdit), но он предназначен только для ввода числовых значений. Пополним библиотеку ещё одним элементом, который позволит вводить в поле любой текст. Элемент «Текстовое поле ввода» может понадобиться в разных ситуациях. Например, можно создать поиск строки в файлах «песочницы» терминала. Еще одна возможность применения — предоставить конечному пользователю MQL-приложения возможность самому ввести массив символов для торговли. Словом, это могут быть любые текстовые данные, необходимые для работы.
Перечислим все составные части, из которых будет собираться элемент «Текстовое поле ввода»:
- Фон
- Ярлык
- Описание
- Поле ввода
Рис. 1. Составные части элемента «Текстовое поле ввода».
Рассмотрим подробнее, как устроен класс этого элемента.
Класс для создания элемента «Текстовое поле ввода»
Создаём файл TextEdit.mqh с классом CTextEdit со стандартными для всех элементов методами и подключаем его к движку библиотеки (файл WndContainer.mqh). В списке ниже перечислены свойства элемента, которые доступны для настройки пользователем:
- Цвет фона элемента
- Ярлыки элемента для активного и заблокированного состояния
- Отступы ярлыка по двум осям (x, y)
- Текст описания элемента
- Отступы текстовой метки по двум осям (x, y)
- Цвет текста в разных состояниях элемента
- Размеры поля ввода
- Отступы поля ввода по двум осям (x, y)
- Цвет поля ввода и текста поля ввода в разных состояниях
- Выравнивание текста в поле ввода (слева/справа/по центру)
- Режим показа указателя выделения текста
- Режим сброса значения в поле ввода
//| Класс для создания текстового поля ввода |
//+------------------------------------------------------------------+
class CTextEdit : public CElement
{
private:
//--- Цвет фона элемента
color m_area_color;
//--- Ярлыки элемента в активном и заблокированном состоянии
string m_icon_file_on;
string m_icon_file_off;
//--- Отступы ярлыка
int m_icon_x_gap;
int m_icon_y_gap;
//--- Текст описания поля ввода
string m_label_text;
//--- Отступы текстовой метки
int m_label_x_gap;
int m_label_y_gap;
//--- Цвета текста в разных состояниях
color m_label_color;
color m_label_color_hover;
color m_label_color_locked;
color m_label_color_array[];
//--- Текущее значение в поле ввода
string m_edit_value;
//--- Размеры поля ввода
int m_edit_x_size;
int m_edit_y_size;
//--- Отступы поля ввода
int m_edit_x_gap;
int m_edit_y_gap;
//--- Цвета поля ввода и текста поля ввода в разных состояниях
color m_edit_color;
color m_edit_color_locked;
color m_edit_text_color;
color m_edit_text_color_locked;
color m_edit_text_color_highlight;
//--- Цвета рамки поля ввода в разных состояниях
color m_edit_border_color;
color m_edit_border_color_hover;
color m_edit_border_color_locked;
color m_edit_border_color_array[];
//--- Режим сброса значения (пустая строка)
bool m_reset_mode;
//--- Режим показа указателя выделения текста
bool m_show_text_pointer_mode;
//--- Режим выравнивания текста
ENUM_ALIGN_MODE m_align_mode;
//---
public:
//--- Отступы ярлыка
void IconXGap(const int x_gap) { m_icon_x_gap=x_gap; }
void IconYGap(const int y_gap) { m_icon_y_gap=y_gap; }
//--- (1) Цвет фона, (2) текст описания поля ввода, (3) отступы текстовой метки
void AreaColor(const color clr) { m_area_color=clr; }
string LabelText(void) const { return(m_label.Description()); }
void LabelText(const string text) { m_label.Description(text); }
void LabelXGap(const int x_gap) { m_label_x_gap=x_gap; }
void LabelYGap(const int y_gap) { m_label_y_gap=y_gap; }
//--- Цвета текстовой метки в разных состояниях
void LabelColor(const color clr) { m_label_color=clr; }
void LabelColorHover(const color clr) { m_label_color_hover=clr; }
void LabelColorLocked(const color clr) { m_label_color_locked=clr; }
//--- (1) Размеры поля ввода, (2) отступ поля ввода от правого края
void EditXSize(const int x_size) { m_edit_x_size=x_size; }
void EditYSize(const int y_size) { m_edit_y_size=y_size; }
//--- Отступы поля ввода
void EditXGap(const int x_gap) { m_edit_x_gap=x_gap; }
void EditYGap(const int y_gap) { m_edit_y_gap=y_gap; }
//--- Цвета поля ввода в разных состояниях
void EditColor(const color clr) { m_edit_color=clr; }
void EditColorLocked(const color clr) { m_edit_color_locked=clr; }
//--- Цвета текста поля ввода в разных состояниях
void EditTextColor(const color clr) { m_edit_text_color=clr; }
void EditTextColorLocked(const color clr) { m_edit_text_color_locked=clr; }
void EditTextColorHighlight(const color clr) { m_edit_text_color_highlight=clr; }
//--- Цвета рамки поля ввода в разных состояниях
void EditBorderColor(const color clr) { m_edit_border_color=clr; }
void EditBorderColorHover(const color clr) { m_edit_border_color_hover=clr; }
void EditBorderColorLocked(const color clr) { m_edit_border_color_locked=clr; }
//--- (1) Режим сброса при нажатии на текстовой метке, (2) режим показа указателя выделения текста
bool ResetMode(void) { return(m_reset_mode); }
void ResetMode(const bool mode) { m_reset_mode=mode; }
void ShowTextPointerMode(const bool mode) { m_show_text_pointer_mode=mode; }
//--- Режим выравнивания текста
void AlignMode(ENUM_ALIGN_MODE mode) { m_align_mode=mode; }
//--- Установка ярлыков для элемента в активном и заблокированном состояниях
void IconFileOn(const string file_path);
void IconFileOff(const string file_path);
};
Режим показа указателя выделения текста означает, что при наведении курсора мыши на текстовое поле он будет дополняться ярлыком, показывающим, что здесь можно ввести текст. Ччтобы это работало, в перечисление ENUM_MOUSE_POINTER был добавлен ещё один идентификатор (MP_TEXT_SELECT).
//| Перечисление типов указателей |
//+------------------------------------------------------------------+
enum ENUM_MOUSE_POINTER
{
MP_CUSTOM =0,
MP_X_RESIZE =1,
MP_Y_RESIZE =2,
MP_XY1_RESIZE =3,
MP_XY2_RESIZE =4,
MP_X_SCROLL =5,
MP_Y_SCROLL =6,
MP_TEXT_SELECT =7
};
Соответствующее дополнение было внесено и в класс CPointer (см. листинг кода ниже). Картинка для ярлыка указателя выделения текста доступна в архиве в конце статьи.
//| Pointer.mqh |
//| Copyright 2015, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//--- Ресурсы
...
#resource "\\Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp"
//+------------------------------------------------------------------+
//| Установка картинок для указателя по типу указателя |
//+------------------------------------------------------------------+
void CPointer::SetPointerBmp(void)
{
switch(m_type)
{
case MP_X_RESIZE :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_x_rs_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_x_rs.bmp";
break;
case MP_Y_RESIZE :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_y_rs_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_y_rs.bmp";
break;
case MP_XY1_RESIZE :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs.bmp";
break;
case MP_XY2_RESIZE :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs.bmp";
break;
case MP_X_SCROLL :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll.bmp";
break;
case MP_Y_SCROLL :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_y_scroll_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_y_scroll.bmp";
break;
case MP_TEXT_SELECT :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp";
break;
}
//--- Если указан пользовательский тип (MP_CUSTOM)
if(m_file_on=="" || m_file_off=="")
::Print(__FUNCTION__," > Для указателя курсора должны быть установлены обе картинки!");
}
Для создания элемента «Текстовое поле ввода» понадобятся пять приватных (private) методов и один публичный (public):
{
private:
//--- Объекты для создания поля ввода
CRectLabel m_area;
CBmpLabel m_icon;
CLabel m_label;
CEdit m_edit;
CPointer m_text_select;
//---
public:
//--- Методы для создания текстового поля ввода
bool CreateTextEdit(const long chart_id,const int subwin,const string label_text,const int x,const int y);
//---
private:
bool CreateArea(void);
bool CreateIcon(void);
bool CreateLabel(void);
bool CreateEdit(void);
bool CreateTextSelectPointer(void);
};
В остальном в классе CTextEdit больше нет ничего такого, что не рассматривалось бы в предыдущих статьях этой серии. Поэтому с его возможностями вы сможете ознакомиться самостоятельно. Текущая версия элемента «Текстовое поле ввода» имеет ограничение в 63 символа.
Элемент «Слайдер картинок»
Слайдер картинок относится к информационным элементам графического интерфейса. С его помощью можно создать краткое справочное руководство, где на иллюстрациях показываются какие-либо ситуации на графиках цены или краткие пояснения о предназначении того или иного элемента управления графического интерфейса в используемом MQL-приложении.
Перечислим все составные части, из которых будет собираться элемент «Слайдер картинок».
- Фон
- Кнопки последовательного переключения картинок
- Группа радио-кнопок
- Группа картинок, связанная с группой радио-кнопок
Рис. 2. Составные части элемента «Слайдер картинок».
Класс для создания элемента «Слайдер картинок»
В директории, где содержатся все остальные файлы элементов библиотеки, создаём файл PicturesSlider.mqh со стандартными методами, которые есть во всех других элементах, и подключаем его к файлу WndContainer.mqh. Перечислим в списке ниже свойства элемента, которые доступны для настройки пользователем библиотеки.
- Цвет фона элемента
- Цвет рамки фона элемента
- Отступ для картинок по оси Y
- Отступы для кнопок последовательного переключения картинок по двум осям (x, y)
- Отступы для радио-кнопок по двум осям (x, y)
- Отступ между радио-кнопками
//| Класс для создания слайдера картинок |
//+------------------------------------------------------------------+
class CPicturesSlider : public CElement
{
private:
//--- Цвет фона и рамки элемента
color m_area_color;
color m_area_border_color;
//--- Отступ для картинок по оси Y
int m_pictures_y_gap;
//--- Отступы для кнопок
int m_arrows_x_gap;
int m_arrows_y_gap;
//--- Отступы для радио-кнопок
int m_radio_buttons_x_gap;
int m_radio_buttons_y_gap;
int m_radio_buttons_x_offset;
//---
public:
//--- (1) Цвет фона и (2) рамки фона
void AreaColor(const color clr) { m_area_color=clr; }
void AreaBorderColor(const color clr) { m_area_border_color=clr; }
//--- Отступы для кнопок-стрелок
void ArrowsXGap(const int x_gap) { m_arrows_x_gap=x_gap; }
void ArrowsYGap(const int y_gap) { m_arrows_y_gap=y_gap; }
//--- Отступ для картинок по оси Y
void PictureYGap(const int y_gap) { m_pictures_y_gap=y_gap; }
//--- (1) Отступы радио-кнопок, (2) расстояние между радио-кнопками
void RadioButtonsXGap(const int x_gap) { m_radio_buttons_x_gap=x_gap; }
void RadioButtonsYGap(const int y_gap) { m_radio_buttons_y_gap=y_gap; }
void RadioButtonsXOffset(const int x_offset) { m_radio_buttons_x_offset=x_offset; }
};
Для создания элемента «Слайдер картинок» понадобятся пять приватных (private) методов и один публичный (public):
{
private:
//--- Объекты для создания элемента
CRectLabel m_area;
CBmpLabel m_pictures[];
CRadioButtons m_radio_buttons;
CIconButton m_left_arrow;
CIconButton m_right_arrow;
//---
public:
//--- Методы для создания слайдера картинок
bool CreatePicturesSlider(const long chart_id,const int subwin,const int x,const int y);
//---
private:
bool CreateArea(void);
bool CreatePictures(void);
bool CreateRadioButtons(void);
bool CreateLeftArrow(void);
bool CreateRightArrow(void);
};
Ширина элемента будет рассчитываться автоматически, исходя из установленных пользователем параметров. К таким параметрам относится отступ группы радио-кнопок от левого края элемента, относительно которого также рассчитывается координата для правой кнопки-переключателя картинок слайдера. Высота элемента тоже зависит от размера картинок. Предполагается, что размер картинок будет одинаковый, поэтому в расчётах используются размеры первой картинки в группе.
Перед тем, как вызвать главный метод создания элемента, нужно добавить картинки в массив с помощью метода CPicturesSlider::AddPicture(). Если не передать в качестве единственного аргумента этого метода путь к изображению, будет применяться путь по умолчанию.
//| PicturesSlider.mqh |
//| Copyright 2016, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
...
//--- Картинка по умолчанию
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"
//+------------------------------------------------------------------+
//| Класс для создания слайдера картинок |
//+------------------------------------------------------------------+
class CPicturesSlider : public CElement
{
private:
//--- Массив картинок (путь к картинкам)
string m_file_path[];
//--- Путь к картинке по умолчанию
string m_default_path;
//---
public:
//--- Добавляет картинку
void AddPicture(const string file_path="");
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CPicturesSlider::CPicturesSlider(void) : m_default_path("Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"),
m_area_color(clrNONE),
m_area_border_color(clrNONE),
m_arrows_x_gap(2),
m_arrows_y_gap(2),
m_radio_button_width(12),
m_radio_buttons_x_gap(25),
m_radio_buttons_y_gap(1),
m_radio_buttons_x_offset(20),
m_pictures_y_gap(25)
{
//--- Сохраним имя класса элемента в базовом классе
CElement::ClassName(CLASS_NAME);
//--- Установим приоритеты на нажатие левой кнопки мыши
m_zorder=0;
}
//+------------------------------------------------------------------+
//| Добавляет картинку |
//+------------------------------------------------------------------+
void CPicturesSlider::AddPicture(const string file_path="")
{
//--- Увеличим размер массивов на один элемент
int array_size=::ArraySize(m_pictures);
int new_size=array_size+1;
::ArrayResize(m_pictures,new_size);
::ArrayResize(m_file_path,new_size);
//--- Сохраним значения переданных параметров
m_file_path[array_size]=(file_path=="")? m_default_path : file_path;
}
Для показа картинки из группы нужно использовать метод CPicturesSlider::SelectPicture(). Этот метод будет вызываться при нажатии кнопок-переключателей и радио-кнопок в обработчике класса CPicturesSlider.
{
public:
//--- Переключает картинку по указанному индексу
void SelectPicture(const uint index);
};
//+------------------------------------------------------------------+
//| Указывает, какая картинка должна быть показана |
//+------------------------------------------------------------------+
void CPicturesSlider::SelectPicture(const uint index)
{
//--- Получим количество картинок
uint pictures_total=PicturesTotal();
//--- Если нет ни одной картинки в группе, сообщить об этом
if(pictures_total<1)
{
::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, "
"когда в группе есть хотя бы одна картинка! Воспользуйтесь методом CPicturesSlider::AddPicture()");
return;
}
//--- Скорректировать значение индекса, если выходит из диапазона
uint correct_index=(index>=pictures_total)? pictures_total-1 : index;
//--- Выделить радио-кнопку по этому индексу
m_radio_buttons.SelectRadioButton(correct_index);
//--- Переключить картинку
for(uint i=0; i<pictures_total; i++)
{
if(i==correct_index)
m_pictures[i].Timeframes(OBJ_ALL_PERIODS);
else
m_pictures[i].Timeframes(OBJ_NO_PERIODS);
}
}
При нажатии на кнопки для последовательного переключения картинок в обработчике элемента вызываются методы CPicturesSlider::OnClickLeftArrow() и CPicturesSlider::OnClickRightArrow(). В листинге ниже показан код метода для левой кнопки. События нажатия на кнопки слайдера картинок можно при необходимости отследить в пользовательском классе MQL-приложения.
{
public:
private:
//--- Обработка нажатия на левой кнопке
bool OnClickLeftArrow(const string clicked_object);
//--- Обработка нажатия на правой кнопке
bool OnClickRightArrow(const string clicked_object);
};
//+------------------------------------------------------------------+
//| Нажатие на левую кнопку |
//+------------------------------------------------------------------+
bool CPicturesSlider::OnClickLeftArrow(const string clicked_object)
{
//--- Выйдем, если нажатие было не на кнопке
if(::StringFind(clicked_object,CElement::ProgramName()+"_icon_button_",0)<0)
return(false);
//--- Получим идентификатор элемента из имени объекта
int id=CElement::IdFromObjectName(clicked_object);
//--- Получим индекс элемента из имени объекта
int index=CElement::IndexFromObjectName(clicked_object);
//--- Выйти, если идентификаторы элементов не совпадают
if(id!=CElement::Id())
return(false);
//--- Выйти, если индексы элементов не совпадают
if(index!=0)
return(false);
//--- Получим текущий индекс выделенной радио-кнопки
int selected_radio_button=m_radio_buttons.SelectedButtonIndex();
//--- Переключение картинки
SelectPicture(--selected_radio_button);
//--- Отправим сообщение об этом
::EventChartCustom(m_chart_id,ON_CLICK_BUTTON,CElement::Id(),CElement::Index(),"");
return(true);
}
В листинге ниже показан сокращённый код обработчика событий слайдера картинок. Видно, что здесь же отслеживается и нажатие на радио-кнопках слайдера. Понять, что нажатие было на радио-кнопке в локальной группе, можно по идентификатору элемента, который равен идентификатору слайдера картинок.
//| Обработчик событий |
//+------------------------------------------------------------------+
void CPicturesSlider::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Обработка события перемещения курсора
...
//--- Обработка события нажатия на радио-кнопке
if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL)
{
//--- Если это радио-кнопка слайдера, переключить картинку
if(lparam==CElement::Id())
SelectPicture(m_radio_buttons.SelectedButtonIndex());
//---
return;
}
//--- Обработка события нажатия левой кнопки мыши на объекте
if(id==CHARTEVENT_OBJECT_CLICK)
{
//--- Если нажатие на кнопках-стрелках слайдера, переключить картинку
if(OnClickLeftArrow(sparam))
return;
if(OnClickRightArrow(sparam))
return;
//---
return;
}
}
Элементы «Текстовая метка» и «Картинка»
В качестве дополнения в библиотеку включены два новых класса CTextLabel и CPicture для создания простых элементов интерфейса «Текстовая метка» и «Картинка». Они могут использоваться как отдельные объекты, без жёсткой привязки, к какому-либо другому элементу. Их содержание очень простое. В классе CPicture пользователь может изменить только одно свойство – это путь к картинке. Для этого предназначен метод CPicture::Path(). Если не указать свой путь, то будет использоваться картинка по умолчанию. Картинку можно изменить программно в любой момент и после создания графического интерфейса MQL-приложения.
//| Picture.mqh |
//| Copyright 2016, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//--- Ресурсы
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"
//+------------------------------------------------------------------+
//| Класс для создания картинки |
//+------------------------------------------------------------------+
class CPicture : public CElement
{
private:
//--- Путь к картинке
string m_path;
//---
public:
//--- Возвращает/устанавливает путь к картинке
string Path(void) const { return(m_path); }
void Path(const string path);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CPicture::CPicture(void) : m_path("Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp")
{
//--- Сохраним имя класса элемента в базовом классе
CElement::ClassName(CLASS_NAME);
//--- Установим приоритеты на нажатие левой кнопки мыши
m_zorder=0;
}
//+------------------------------------------------------------------+
//| Установка картинки |
//+------------------------------------------------------------------+
void CPicture::Path(const string path)
{
m_path=path;
m_picture.BmpFileOn("::"+path);
m_picture.BmpFileOff("::"+path);
}
Что касается элемента «Текстовая метка», то здесь тоже всё довольно просто и пользователь может установить всего лишь четыре свойства:
- Текст метки
- Цвет текста
- Шрифт
- Размер шрифта
//| Класс для создания текстовой метки |
//+------------------------------------------------------------------+
class CTextLabel : public CElement
{
public:
//--- Возвращает/устанавливает текст метки
string LabelText(void) const { return(m_label.Description()); }
void LabelText(const string text) { m_label.Description(text); }
//--- Установка (1) цвета, (2) шрифта и (3) размера ширифта текстовой метки
void LabelColor(const color clr) { m_label.Color(clr); }
void LabelFont(const string font) { m_label.Font(font); }
void LabelFontSize(const int size) { m_label.FontSize(size); }
};
Класс CFonts для работы со шрифтами
Для более простого выбора шрифта был написан дополнительный класс CFonts. В нём доступны 187 шрифтов. Это системные шрифты терминала, список которых вы уже наверняка видели в настройках некоторых графических объектов.
Рис. 3. Системные шрифты терминала.
Файл со шрифтами (Fonts.mqh) находится в директории "MetaTrader 5\MQL5\Include\EasyAndFastGUI\Fonts.mqh" и для полного доступа во всей схеме библиотеки подключен к файлу Objects.mqh:
//| Objects.mqh |
//| Copyright 2015, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Enums.mqh"
#include "Defines.mqh"
#include "..\Fonts.mqh"
#include "..\Canvas\Charts\LineChart.mqh"
#include <ChartObjects\ChartObjectSubChart.mqh>
#include <ChartObjects\ChartObjectsBmpControls.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>
В классе CFonts есть только два публичных метода для получения размера массива шрифтов и получения названия шрифта по индексу. Массив шрифтов инициализируется в конструкторе класса.
//| Fonts.mqh |
//| Copyright 2016, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс для работы с шрифтом |
//+------------------------------------------------------------------+
class CFonts
{
private:
//--- Массив шрифотов
string m_fonts[];
//---
public:
CFonts(void);
~CFonts(void);
//--- Возвращает количество шрифтов
int FontsTotal(void) const { return(::ArraySize(m_fonts)); }
//--- Возвращает шрифт по индексу
string FontsByIndex(const uint index);
//---
private:
//--- Инициализация массива шрифтов
void InitializeFontsArray(void);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CFonts::CFonts(void)
{
//--- Инициализация массива шрифтов
InitializeFontsArray();
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CFonts::~CFonts(void)
{
::ArrayFree(m_fonts);
}
При вызове метода CFonts::FontsByIndex() осуществляется корректировка, предотвращающая выход за пределы массива:
//| Возвращает шрифт по индексу |
//+------------------------------------------------------------------+
string CFonts::FontsByIndex(const uint index)
{
//--- Размер массива
uint array_size=FontsTotal();
//--- Корректировка в случае выхода из диапазона
uint i=(index>=array_size)? array_size-1 : index;
//--- Вернуть шрифт
return(m_fonts[i]);
}
Список дополнительных обновлений библиотеки
1. Исправлен некорректный показ всплывающих подсказок в диалоговых окнах. Теперь статус кнопки подсказок на главном окне при нажатии распространяется на все окна графического интерфейса. При нажатии на кнопку генерируется сообщение с новым идентификатором события ON_WINDOW_TOOLTIPS (см. файл Defines.mqh).
//| Defines.mqh |
//| Copyright 2015, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
...
#define ON_WINDOW_TOOLTIPS (29) // Нажатие на кнопке "Всплывающие подсказки"
Соответственно, в класс CWindow добавлен метод OnClickTooltipsButton() для обработки нажатия на кнопке всплывающих подсказок:
//| Класс создания формы для элементов управления |
//+------------------------------------------------------------------+
class CWindow : public CElement
{
private:
//--- Обработка нажатия на кнопке "Всплывающие подсказки"
bool OnClickTooltipsButton(const string clicked_object);
};
//+------------------------------------------------------------------+
//| Обработчик событий графика |
//+------------------------------------------------------------------+
void CWindow::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Обработка события нажатия на объекте
if(id==CHARTEVENT_OBJECT_CLICK)
{
//--- Если нажатие на кнопке "Всплывающие подсказки"
if(OnClickTooltipsButton(sparam))
return;
}
}
//+------------------------------------------------------------------+
//| Обработка нажатия на кнопке "Всплывающие подсказки" |
//+------------------------------------------------------------------+
bool CWindow::OnClickTooltipsButton(const string clicked_object)
{
//--- Эта кнопка не нужна, если окно диалоговое
if(m_window_type==W_DIALOG)
return(false);
//--- Выйдем, если нажатие было не на радио-кнопке
if(::StringFind(clicked_object,CElement::ProgramName()+"_window_tooltip_",0)<0)
return(false);
//--- Получим идентификатор элемента из имени объекта
int id=CElement::IdFromObjectName(clicked_object);
//--- Выйти, если идентификаторы элементов не совпадают
if(id!=CElement::Id())
return(false);
//--- Запомним состояние в поле класса
m_tooltips_button_state=m_button_tooltip.State();
//--- Отправим сообщение об этом
::EventChartCustom(m_chart_id,ON_WINDOW_TOOLTIPS,CElement::Id(),CElement::Index(),"");
return(true);
}
Для того, чтобы всё это работало в движке библиотеки (класс CWndEvents), добавлен метод OnWindowTooltips() для обработки события с идентификатором ON_WINDOW_TOOLTIPS:
{
private:
//--- Включение/отключение всплывающих подсказок
bool OnWindowTooltips(void);
};
//+------------------------------------------------------------------+
//| Событие CHARTEVENT_CUSTOM |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventCustom(void)
{
//--- Если сигнал свернуть форму
//--- Если сигнал развернуть форму
//--- Если сигнал изменить размер элементов по оси X
//--- Если сигнал изменить размер элементов по оси Y
//--- Если сигнал включить/выключить всплывающие подсказки
if(OnWindowTooltips())
return;
//--- Если сигнал на скрытие контекстных меню от пункта инициатора
//--- Если сигнал на скрытие всех контекстных меню
//--- Если сигнал на открытие диалогового окна
//--- Если сигнал на закрытие диалогового окна
//--- Если сигнал на сброс цвета элементов на указанной форме
//--- Если сигнал на сброс приоритетов на нажатие левой кнопкой мыши
//--- Если сигнал на восстановление приоритетов на нажатие левой кнопкой мыши
}
//+------------------------------------------------------------------+
//| Событие ON_WINDOW_TOOLTIPS |
//+------------------------------------------------------------------+
bool CWndEvents::OnWindowTooltips(void)
{
//--- Если сигнал "Включить/отключить всплывающие подсказки"
if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_TOOLTIPS)
return(false);
//--- Если идентификаторы окна совпадают
if(m_lparam!=m_windows[0].Id())
return(true);
//--- Синхронизировать режим всплывающих подсказок между всеми окнами
int windows_total=WindowsTotal();
for(int w=0; w<windows_total; w++)
{
if(w>0)
m_windows[w].TooltipButtonState(m_windows[0].TooltipButtonState());
}
//---
return(true);
}
2. Добавлена возможность изменять текст в описании следующих элементов после их создания:
Рис. 4. Список элементов с возможностью изменения текста после создания.
3. Во всех элементах, где это могло понадобиться, теперь можно установить картинку (см. таблицу ниже). Кроме этого, добавлена возможность изменять картинки элементов уже после их создания:
Рис. 5. Список элементов с возможностью изменения картинки после создания.
Для замены картинки во всех перечисленных в таблице выше элементах есть методы IconFileOn() и IconFileOff().
4. Добавлена возможность программно управлять состоянием всех типов кнопок и вкладок (нажата/отжата) после их создания. В таблице ниже показаны элементы, к которым относится это дополнение:
Рис. 6. Список элементов с возможностью изменения состояния (нажата/отжата) после создания.
5. Оптимизирован алгоритм подсветки пунктов при наведении курсора мыши в следующих элементах:
Рис. 7. Элементы с оптимизацией алгоритма подсветки пунктов элемента.
Ранее в списках пунктов элементов из таблицы выше программа в цикле проходила по всем пунктам, проверяя расположение курсора мыши над ними. Таким образом, пункт под курсором выделялся другим цветом, а для остальных устанавливался цвет по умолчанию. Но такой метод очень ресурсоемкий, поэтому здесь было необходимо провести оптимизацию. Теперь вместо цикла по всему массиву пунктов в изменении цвета участвуют только два пункта. Поиск в цикле осуществляется только когда был замечен переход на другой пункт, то есть фокус изменился.
Далее в качестве примера рассмотрим, как это было реализовано в классе CListView. Для реализации описанного выше нужно было добавить (1) поле класса m_prev_item_index_focus для сохранения индекса последнего в фокусе пункта, (2) метод CListView::CheckItemFocus() для проверки фокуса над пунктом и (3) изменить алгоритм в методе CListView::ChangeItemsColor().
//| Класс для создания списка |
//+------------------------------------------------------------------+
class CListView : public CElement
{
private:
//--- Для определения момента перехода курсора мыши с одного пункта на другой
int m_prev_item_index_focus;
//---
private:
//--- Изменение цвета пунктов списка при наведении
void ChangeItemsColor(void);
//--- Проверка фокуса строки списка при наведении курсора
void CheckItemFocus(void);
};
Метод CListView::CheckItemFocus() вызывается только в том случае, если курсор мыши зашёл в область элемента (в данном случае списка – CListView), а также когда осуществлён переход курсора мыши с одного пункта на другой (см. листинг кода ниже). Как только пункт, над которым находится курсор мыши, найден, его индекс сохраняется.
//| Проверка фокуса строки списка при наведении курсора |
//+------------------------------------------------------------------+
void CListView::CheckItemFocus(void)
{
//--- Получим текущую позицию ползунка полосы прокрутки
int v=m_scrollv.CurrentPos();
//--- Определим, над каким пунктом сейчас находится курсор, и подсветим его
for(int i=0; i<m_visible_items_total; i++)
{
//--- Увеличим счётчик, если внутри диапазона списка
if(v>=0 && v<m_items_total)
v++;
//--- Пропускаем выделенный пункт
if(m_selected_item_index==v-1)
{
m_items[i].BackColor(m_item_color_selected);
m_items[i].Color(m_item_text_color_selected);
continue;
}
//--- Если курсор над этим пунктом, то подсветим его
if(m_mouse.X()>m_items[i].X() && m_mouse.X()<m_items[i].X2() &&
m_mouse.Y()>m_items[i].Y() && m_mouse.Y()<m_items[i].Y2())
{
m_items[i].BackColor(m_item_color_hover);
m_items[i].Color(m_item_text_color_hover);
//--- Запомнить пункт
m_prev_item_index_focus=i;
break;
}
}
}
Вызов метода CListView::CheckItemFocus() осуществляется в методе CListView::ChangeItemsColor() в случаях, как это было описано в предыдущем абзаце (см. листинг кода ниже):
//| Изменение цвета строки списка при наведении курсора |
//+------------------------------------------------------------------+
void CListView::ChangeItemsColor(void)
{
//--- Выйдем, если отключена подсветка при наведении курсора или прокрутка списка активна
if(!m_lights_hover || m_scrollv.ScrollState())
return;
//--- Выйдем, если элемент не выпадающий и форма заблокирована
if(!CElement::IsDropdown() && m_wnd.IsLocked())
return;
//--- Если зашли в список заново
if(m_prev_item_index_focus==WRONG_VALUE)
{
//--- Проверка фокуса на текущем пункте
CheckItemFocus();
}
else
{
//--- Проверим фокус на текущем ряде
int i=m_prev_item_index_focus;
bool condition=m_mouse.X()>m_items[i].X() && m_mouse.X()<m_items[i].X2() &&
m_mouse.Y()>m_items[i].Y() && m_mouse.Y()<m_items[i].Y2();
//--- Если переход на другой пункт
if(!condition)
{
//--- Сбросить цвет предыдущего пункта
m_items[i].BackColor(m_item_color);
m_items[i].Color(m_item_text_color);
m_prev_item_index_focus=WRONG_VALUE;
//--- Проверка фокуса на текущем пункте
CheckItemFocus();
}
}
}
В обработчике событий CListView::OnEvent() вызов метода CListView::ChangeItemsColor() осуществляется только если курсор мыши находится в области элемента. Как только курсор вышел из области элемента, устанавливаются цвета по умолчанию и сбрасывается значение индекса пункта. Ниже показана сокращённая версия обработчика событий.
//| Обработчик событий |
//+------------------------------------------------------------------+
void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Обработка события перемещения курсора
if(id==CHARTEVENT_MOUSE_MOVE)
{
//--- Выйти, если элемент скрыт
//--- Выйти, если номера подокон не совпадают
//--- Проверка фокуса над элементами
//--- Если это выпадающий список и кнопка мыши нажата
//--- Смещаем список, если управление ползунком полосы прокрутки в действии
//--- Сбросить цвета элемента, если не в фокусе
if(!CElement::MouseFocus())
{
//--- Если уже есть пункт в фокусе
if(m_prev_item_index_focus!=WRONG_VALUE)
{
//--- Сбросить цвета списка
ResetColors();
m_prev_item_index_focus=WRONG_VALUE;
}
return;
}
//--- Изменяет цвет строк списка при наведении
ChangeItemsColor();
return;
}
}
Такой же принцип реализован в классах CTable, CCalendar и CTreeView, но с некоторыми отличиями, учитывающими особенности каждого элемента.
6. При нажатии на кнопку типа CIconButton в двойном режиме (когда кнопка не отжимается после нажатия) показывается другая картинка, если она установлена. Картинку для нажатой кнопки можно установить с помощью методов CIconButton::IconFilePressedOn() и CIconButton::IconFilePressedOff().
//| Класс для создания кнопки с картинкой |
//+------------------------------------------------------------------+
class CIconButton : public CElement
{
private:
//--- Ярлыки кнопки в активном, заблокированном и нажатом состоянии
string m_icon_file_on;
string m_icon_file_off;
string m_icon_file_pressed_on;
string m_icon_file_pressed_off;
//---
public:
//--- Установка ярлыков для кнопки в нажатом, активном и заблокированном состояниях
void IconFileOn(const string file_path);
void IconFileOff(const string file_path);
void IconFilePressedOn(const string file_path);
void IconFilePressedOff(const string file_path);
};
//+------------------------------------------------------------------+
//| Установка картинки для нажатого состояния "Включено" |
//+------------------------------------------------------------------+
void CIconButton::IconFilePressedOn(const string file_path)
{
//--- Выйти, если у кнопки отключен режим "Два состояния"
if(!m_two_state)
return;
//--- Сохранить путь к картинке
m_icon_file_pressed_on=file_path;
//--- Сразу установить, если кнопка нажата
if(m_button.State())
m_icon.BmpFileOn("::"+file_path);
}
//+------------------------------------------------------------------+
//| Установка картинки для нажатого состояния "Отключено" |
//+------------------------------------------------------------------+
void CIconButton::IconFilePressedOff(const string file_path)
{
//--- Выйти, если у кнопки отключен режим "Два состояния"
if(!m_two_state)
return;
//--- Сохранить путь к картинке
m_icon_file_pressed_off=file_path;
//--- Сразу установить, если кнопка нажата
if(m_button.State())
m_icon.BmpFileOff("::"+file_path);
}
7. Добавлена возможность программно выделять ряд в таблице (CTable). Для этого воспользуйтесь методом CTable::SelectRow(). Указание индекса уже выделенного ряда снимает выделение.
//| Класс для создания таблицы из полей ввода |
//+------------------------------------------------------------------+
class CTable : public CElement
{
public:
//--- Выделение указанного ряда таблицы
void SelectRow(const uint row_index);
};
//+------------------------------------------------------------------+
//| Выделение указанного ряда таблицы |
//+------------------------------------------------------------------+
void CTable::SelectRow(const uint row_index)
{
//--- Корректировка в случае выхода из диапазона
uint index=(row_index>=(uint)m_rows_total)? m_rows_total-1 : row_index;
//--- Если этот ряд уже выделен, снимем выделение
bool is_selected=(index==m_selected_item);
//--- Сохраним индекс ряда
m_selected_item=(is_selected)? WRONG_VALUE : (int)index;
//--- Сохраним строку ячейки
m_selected_item_text=(is_selected)? "" : m_vcolumns[0].m_vrows[index];
//--- Сформировать строку с параметрами ячейки
string cell_params=string(0)+"_"+string(index)+"_"+m_vcolumns[0].m_vrows[index];
//--- Сброс фокуса
m_prev_item_index_focus=WRONG_VALUE;
//--- Обновляет таблицу
UpdateTable();
//--- Подсветка выделенного ряда
HighlightSelectedItem();
}
8. Устранена недоработка в отображении элементов выделенной вкладки элемента типа CIconTabs. Проблема возникала при открытии и разворачивании формы с этим типом вкладок.
Приложение для теста элемента
Напишем тестовое приложение, в котором вы сможете протестировать все новые элементы и самостоятельно попрактиковаться, испытав различные их режимы. В графическом интерфейсе приложения создадим элемент «Вкладки» (класс CTabs), в котором будет четыре вкладки со следующим содержанием:
1. Первая вкладка:
- Прогресс-бар (CProgressBar).
- Текстовое поле ввода (CTextEdit).
- Комбо-бокс с выпадающим списком (CCombobox).
- Поле ввода для числовых значений (CSpinEdit).
- Кнопка для вызова цветовой палитры (CColorButton).
- Текстовая метка (CTextLabel).
Для всех элементов, кроме текстовой метки, установим ярлык. У элементов «Прогресс-бар» и «Текстовое поле ввода» через определённые временные интервалы будем изменять текстовое описание элемента, чтобы продемонстрировать, что такая возможность теперь доступна. В список комбо-бокса поместим названия всех шрифтов из класса CFonts. Событийную модель тестового MQL-приложения настроим таким образом, чтобы выбор шрифта из списка комбо-бокса отражался на текстовой метке. Точно так же свяжем с текстовой меткой числовое поле ввода для изменения размера шрифта и выбор цвета в палитре.
Обработчик событий для управления параметрами элемента «Текстовая метка» тогда будет выглядеть так:
//| Обработчик событий графика |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Событие выбора в списке
if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
{
//--- Если идентификаторы элементов совпадают
if(lparam==m_combobox1.Id())
{
//--- Изменить шрифт
m_text_label1.LabelFont(m_combobox1.ButtonText());
}
//---
return;
}
//--- Событие нажатия на кнопках поля ввода
if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC ||
id==CHARTEVENT_CUSTOM+ON_CLICK_DEC)
{
//--- Если идентификаторы элементов совпадают
if(lparam==m_spin_edit1.Id())
{
//--- Изменить размер шрифта
m_text_label1.LabelFontSize(int(m_spin_edit1.GetValue()));
}
//---
return;
}
//--- Событие изменения цвета посредством цветовой палитры
if(id==CHARTEVENT_CUSTOM+ON_END_EDIT)
{
//--- Если идентификаторы элементов совпадают
if(lparam==m_spin_edit1.Id())
{
//--- Изменить размер шрифта
m_text_label1.LabelFontSize(int(m_spin_edit1.GetValue()));
}
//---
return;
}
//--- Событие изменения цвета посредством цветовой палитры
if(id==CHARTEVENT_CUSTOM+ON_CHANGE_COLOR)
{
//--- Если идентификаторы элементов совпадают
if(lparam==m_color_picker.Id())
{
//--- Если ответ от первой кнопки
if(sparam==m_color_button1.LabelText())
{
//--- Изменить цвет объекта
m_text_label1.LabelColor(m_color_button1.CurrentColor());
return;
}
}
return;
}
//--- Событие нажатия на кнопке
if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
{
//--- Если нажали на первую кнопку для вызова цветовой палитры
if(sparam==m_color_button1.LabelText())
{
//--- Передать указатель кнопки, что автоматически откроет окно с цветовой палитрой
m_color_picker.ColorButtonPointer(m_color_button1);
return;
}
//---
return;
}
}
На скриншоте ниже показано, как в итоге можно настроить отображение текста посредством графического интерфейса, созданного разрабатываемой библиотекой:
Рис. 8. Группа элементов на первой вкладке.
2. На второй вкладке поместим только один элемент – «Слайдер картинок» (класс CPicturesSlider). Добавим в группу только три картинки по умолчанию, чтобы вы могли быстро протестировать этот элемент самостоятельно. Для корректной работы элемента подготовьте картинки одинакового размера.
Рис. 9. Элемент «Слайдер картинок» на второй вкладке.
Для программного переключения картинок воспользуйтесь методом CPicturesSlider::SelectPicture().
3. На третьей вкладке будет таблица типа CTable. Для программного выделения ряда воспользуйтесь методом CTable::SelectRow().
Рис. 10. Элемент «Таблица» на третьей вкладке.
4. На четвёртой вкладке расположим три элемента: (1) календарь, (2) выпадающий календарь и (3) кнопку с двумя разными картинками для состояний нажата/отжата.
Рис. 11. Группа элементов на четвёртой вкладке.
В конце статьи Вы можете загрузить к себе на компьютер это тестовое приложение для более подробного изучения.
Заключение
На текущем этапе разработки библиотеки для создания графических интерфейсов её общая схема выглядит так, как показано на рисунке ниже.
Рис. 12. Структура библиотеки на текущей стадии разработки.
В следующей версии библиотека будет расширена дополнительными элементами управления. Также мы разовьем и дополним новыми возможностями уже существующие элементы.
При возникновении вопросов по использованию материала, предоставленного в этих файлах, вы можете обратиться к подробному описанию процесса разработки библиотеки в одной из статей этой серии или задать вопрос в комментариях к статье.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Есть какое то ограничение на количество символов в CTextLabel ? У меня не выводится весь текст который задаю m_text_label_Info.LabelText("...");
Из статьи:
В остальном в классе CTextEdit больше нет ничего такого, что не рассматривалось бы в предыдущих статьях этой серии. Поэтому с его возможностями вы сможете ознакомиться самостоятельно. Текущая версия элемента «Текстовое поле ввода» имеет ограничение в 63 символа.
Во всех графических объектах терминала есть такое ограничение. Обойти можно только, если выводить текст рисуя его на объекте типа OBJ_BITMAP_LABEL.
В следующей версии библиотеки все элементы будут рисоваться, поэтому такого ограничения не будет.
Из статьи:
Во всех графических объектах терминала есть такое ограничение. Обойти можно только, если выводить текст рисуя его на объекте типа OBJ_BITMAP_LABEL.
В следующей версии библиотеки все элементы будут рисоваться, поэтому такого ограничения не будет.
понял, спасибо за ответ, извиняюсь за невнимательность