
Графические интерфейсы IV: Информационные элементы интерфейса (Глава 1)
Содержание
- Введение
- Элемент «Статусная строка»
- Тест строки состояния
- Элемент «Всплывающая подсказка»
- Тест всплывающих подсказок
- Персональный массив для всплывающих подсказок
- Заключение
Введение
О том, для чего предназначена эта библиотека, более подробно можно прочитать в самой первой статье: Графические интерфейсы I: Подготовка структуры библиотеки (Глава 1). В конце статей каждой части представлен список глав со ссылками. Там же есть возможность загрузить к себе на компьютер полную версию библиотеки на текущей стадии разработки. Файлы нужно разместить по тем же директориям, как они расположены в архиве.
На текущий момент в разрабатываемой библиотеке для создания графических интерфейсов есть форма и несколько элементов управления, которые можно к ней присоединять. Ранее уже упоминалось о том, что в одной из следующих статей будем рассматривать многооконный режим. Сейчас всё готово, чтобы заняться этим вопросом, и мы приступим к нему во второй главе этой статьи. А перед этим, в первой главе, напишем классы, с помощью которых можно будет создавать информационные элементы интерфейса, такие как «статусная строка» и «всплывающая подсказка».
Элемент «Статусная строка»
«Статусная строка» (или «строка состояния») относится к информационным элементам графического интерфейса. Этот элемент предназначен для оперативного вывода каких-либо важных данных, сведений, подсказок и т.д. Например, в терминалах MetaTrader тоже есть строка состояния. Она состоит из нескольких разделов (пунктов). В первом отображается информация о том, в каком разделе терминала сейчас находится курсор мыши или названия команд программы. Также есть пункты, где отображаются дата и цены, когда курсор перемещается в области графика цен. Некоторые пункты содержат контекстное меню, которое вызывается нажатием левой кнопкой мыши. В редакторе кода MetaEditor тоже есть строка состояния. В её пунктах также отображаются подсказки команд программы, местоположение курсора (строка/столбец) и режим ввода текста (INS/OVR). Подробнее о статусных строках терминала и редактора можно прочитать в их справках (F1).
В этой статье мы создадим простой вариант строки состояния, без возможности присоединения к её пунктам контекстных меню. Так же, как и другие элементы интерфейса, статусная строка будет собираться из нескольких объектов-примитивов:
- Фон
- Пункты
- Разделительные линии
Рис. 1. Составные части элемента «Статусная строка».
Создайте файл StatusBar.mqh и подключите его к файлу WndContainer.mqh, чтобы он был доступен в дальнейшем для использования во всей библиотеке. В листинге кода ниже показан класс CStatusBar со стандартными виртуальными методами, которые используются во всех классах элементов управления.
//+------------------------------------------------------------------+ //| StatusBar.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" //+------------------------------------------------------------------+ //| Класс для создания статусной строки | //+------------------------------------------------------------------+ class CStatusBar : public CElement { private: //--- Указатель на форму, к которой элемент присоединён CWindow *m_wnd; //--- public: CStatusBar(void); ~CStatusBar(void); //--- Сохраняет указатель формы void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } //--- public: //--- Обработчик событий графика virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) {} //--- Таймер virtual void OnEventTimer(void) {} //--- Перемещение элемента virtual void Moving(const int x,const int y); //--- (1) Показ, (2) скрытие, (3) сброс, (4) удаление virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- (1) Установка, (2) сброс приоритетов на нажатие левой кнопки мыши virtual void SetZorders(void); virtual void ResetZorders(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CStatusBar::CStatusBar(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CStatusBar::~CStatusBar(void) { } //+------------------------------------------------------------------+
Методы для обработки событий OnEvent() и OnEventTimer() в этой версии не будут использоваться и оставлены для единообразия с другими классами элементов управления.
Далее рассмотрим свойства статусной строки. Поскольку предполагаются массивы объектов, то здесь будут общие и уникальные свойства для них. Из уникальных указывается только ширина пунктов. Список общих свойств:
- Цвет фона и рамки фона
- Цвет текста
- Цвета для разделительной линии
- Приоритет на нажатие левой кнопкой мыши
Инициализируем поля свойств в конструкторе значениями по умолчанию. При создании элемента пользователь может их переопределить с помощью публичных методов класса.
class CStatusBar : public CElement { private: //--- Свойства: // Массивы для уникальных свойств int m_width[]; //--- (1) Цвет фона и (2) рамки фона color m_area_color; color m_area_border_color; //--- Цвет текста color m_label_color; //--- Приоритет на нажатие левой кнопки мыши int m_zorder; //--- Цвета для разделительных линий color m_sepline_dark_color; color m_sepline_light_color; //--- public: //--- Цвет (1) фона, (2) рамки фона и (3) текста void AreaColor(const color clr) { m_area_color=clr; } void AreaBorderColor(const color clr) { m_area_border_color=clr; } void LabelColor(const color clr) { m_label_color=clr; } //--- Цвета разделительных линий void SeparateLineDarkColor(const color clr) { m_sepline_dark_color=clr; } void SeparateLineLightColor(const color clr) { m_sepline_light_color=clr; } }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CStatusBar::CStatusBar(void) : m_area_color(C'240,240,240'), m_area_border_color(clrSilver), m_label_color(clrBlack), m_sepline_dark_color(C'160,160,160'), m_sepline_light_color(clrWhite) { //--- Сохраним имя класса элемента в базовом классе CElement::ClassName(CLASS_NAME); //--- Установим приоритеты на нажатие левой кнопки мыши m_zorder=2; }
Рассмотрим методы для создания статусной строки. Для создания фона будем использовать объект-примитив типа CRectLabel. Для пунктов нужно объявить динамический массив объектов-примитивов типа CEdit. Для создания разделительных линий ранее был создан класс CSeparateLine, который может использоваться и как независимый элемент интерфейса. К примеру, сейчас мы будем использовать его в качестве составной части строки состояния, и нам понадобится массив таких элементов.
Перед созданием строки состояния нужно обязательно добавить пункты с помощью метода CStatusBar::AddItem(), иначе создание графического интерфейса прервётся. Получить количество пунктов можно, вызвав метод CStatusBar::ItemsTotal().
//+------------------------------------------------------------------+ //| StatusBar.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "SeparateLine.mqh" //+------------------------------------------------------------------+ //| Класс для создания статусной строки | //+------------------------------------------------------------------+ class CStatusBar : public CElement { private: //--- Объекты для создания кнопки CRectLabel m_area; CEdit m_items[]; CSeparateLine m_sep_line[]; //--- public: //--- Методы для создания статусной строки bool CreateStatusBar(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateArea(void); bool CreateItems(void); bool CreateSeparateLine(const int line_number,const int x,const int y); //--- public: //--- Количество пунктов int ItemsTotal(void) const { return(::ArraySize(m_items)); } //--- Добавляет пункт с указанными свойствами до создания статусной строки void AddItem(const int width); };
Из всех этих методов рассмотрим подробнее только метод CStatusBar::CreateItems() для создания пунктов статусной строки. Остальные методы не содержат в себе ничего нового, чего мы не рассмотрели бы ранее.
Пункты устанавливаются внутри области фона таким образом, чтобы не перекрывать его рамку. Поэтому в самом начале координатам добавляются отступы в один пиксель. Затем идёт проверка на количество пунктов. Если окажется, что до сих пор не установлено ни одного пункта, то в журнал выводится сообщение об этом, и создание графического интерфейса прерывается.
Сделаем так, что если ширина первого пункта не задана, то она будет рассчитана автоматически следующим образом. Предполагается, что ширина статусной строки равна ширине формы (минус 2 пикселя, чтобы быть внутри области), к которой её присоединяют. Значит, чтобы получить ширину для первого пункта, нужно в цикле суммировать размеры (ширину) остальных пунктов и это значение отнять от ширины формы.
Далее идёт цикл, в котором создаются пункты строки состояния, а после него — цикл для создания разделительных линий. Координаты для разделительных линий рассчитываются относительно координат каждого пункта. Для первого пункта разделительная линия не создаётся. Таким образом количество разделительных линий всегда на один объект меньше количества пунктов.
//+------------------------------------------------------------------+ //| Создаёт список пунктов статусной строки | //+------------------------------------------------------------------+ bool CStatusBar::CreateItems(void) { int l_w=0; int l_x=m_x+1; int l_y=m_y+1; //--- Получим количество пунктов int items_total=ItemsTotal(); //--- Если нет ни одного пункта в группе, сообщить об этом и выйти if(items_total<1) { ::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, " "когда в группе есть хотя бы один пункт! Воспользуйтесь методом CStatusBar::AddItem()"); return(false); } //--- Если ширина первого пункта не задана, то... if(m_width[0]<1) { //--- ...рассчитаем её относительно общей ширины других пунктов for(int i=1; i<items_total; i++) l_w+=m_width[i]; //--- m_width[0]=m_wnd.XSize()-l_w-(items_total+2); } //--- Создадим указанное количество пунктов for(int i=0; i<items_total; i++) { //--- Формирование имени объекта string name=CElement::ProgramName()+"_statusbar_edit_"+string(i)+"__"+(string)CElement::Id(); //--- Координата X l_x=(i>0)? l_x+m_width[i-1] : l_x; //--- Создание объекта if(!m_items[i].Create(m_chart_id,name,m_subwin,l_x,l_y,m_width[i],m_y_size-2)) return(false); //--- Установка свойств m_items[i].Description(""); m_items[i].TextAlign(ALIGN_LEFT); m_items[i].Font(FONT); m_items[i].FontSize(FONT_SIZE); m_items[i].Color(m_label_color); m_items[i].BorderColor(m_area_color); m_items[i].BackColor(m_area_color); m_items[i].Corner(m_corner); m_items[i].Anchor(m_anchor); m_items[i].Selectable(false); m_items[i].Z_Order(m_zorder); m_items[i].ReadOnly(true); m_items[i].Tooltip("\n"); //--- Отступы от крайней точки панели m_items[i].XGap(l_x-m_wnd.X()); m_items[i].YGap(l_y-m_wnd.Y()); //--- Координаты m_items[i].X(l_x); m_items[i].Y(l_y); //--- Размеры m_items[i].XSize(m_width[i]); m_items[i].YSize(m_y_size-2); //--- Сохраним указатель объекта CElement::AddToArray(m_items[i]); } //--- Создание разделительных линий for(int i=1; i<items_total; i++) { //--- Координата X l_x=m_items[i].X(); //--- Создание линии CreateSeparateLine(i,l_x,l_y+2); } //--- return(true); }
Конечно же, понадобится публичный метод для изменения текста в каждом пункте. Создадим такой метод и назовём его CStatusBar::ValueToItem(). В качестве параметров он будет принимать номер индекса пункта и строку, которую нужно в нём отобразить.
class CStatusBar : public CElement { public: //--- Установка значения по указанному индексу void ValueToItem(const int index,const string value); }; //+------------------------------------------------------------------+ //| Устанавливает значение по указанному индексу | //+------------------------------------------------------------------+ void CStatusBar::ValueToItem(const int index,const string value) { //--- Проверка на выход из диапазона int array_size=::ArraySize(m_items); if(array_size<1 || index<0 || index>=array_size) return; //--- Установка переданного текста m_items[index].Description(value); }
Тест строки состояния
Всё готово для того, чтобы протестировать элемент «Строка состояния». Для теста возьмём первого эксперта из предыдущей части (3) серии. Оставим в нём главное меню и пять кнопок типа CIconButton, а все остальные элементы управления удалим. Файл StatusBar.mqh уже подключен к библиотеке, и теперь в пользовательском классе CProgram можно создать его экземпляр и метод для создания строки состояния.
Создадим строку состояния из двух пунктов. Ширину для первого пункта не будем указывать и тогда она рассчитается автоматически. После создания статусной строки, в качестве примера, в первом пункте установим текст «For Help press F1».
//+------------------------------------------------------------------+ //| Класс для создания приложения | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { private: //--- Статусная строка CStatusBar m_status_bar; //--- private: //--- Статусная строка #define STATUSBAR1_GAP_X (1) #define STATUSBAR1_GAP_Y (175) bool CreateStatusBar(void); }; //+------------------------------------------------------------------+ //| Создаёт торговую панель | //+------------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- Создание формы 1 для элементов управления //--- Создание элементов управления: // Главное меню //--- Контекстные меню //--- Создание статусной строки if(!CreateStatusBar()) return(false); //--- Перерисовка графика m_chart.Redraw(); return(true); } //+------------------------------------------------------------------+ //| Создаёт статусную строку | //+------------------------------------------------------------------+ bool CProgram::CreateStatusBar(void) { #define STATUS_LABELS_TOTAL 2 //--- Передать объект панели m_status_bar.WindowPointer(m_window1); //--- Координаты int x=m_window1.X()+STATUSBAR1_GAP_X; int y=m_window1.Y()+STATUSBAR1_GAP_Y; //--- Ширина int width[]={0,110}; //--- Установим свойства перед созданием m_status_bar.YSize(24); //--- Укажем сколько должно быть частей и установим им свойства for(int i=0; i<STATUS_LABELS_TOTAL; i++) m_status_bar.AddItem(width[i]); //--- Создадим элемент управления if(!m_status_bar.CreateStatusBar(m_chart_id,m_subwin,x,y)) return(false); //--- Установка текста в первый пункт статусной строки m_status_bar.ValueToItem(0,"For Help, press F1"); //--- Добавим объект в общий массив групп объектов CWndContainer::AddToElementsArray(0,m_status_bar); return(true); }
Во втором пункте будем отображать локальное время компьютера. Для этого в таймер приложения добавьте код, как это показано в листинге ниже. То есть, обновление пункта будет происходить каждые 500 миллисекунд.
//+------------------------------------------------------------------+ //| Таймер | //+------------------------------------------------------------------+ void CProgram::OnTimerEvent(void) { CWndEvents::OnTimerEvent(); //--- Обновление второго пункта статусной строки будет каждые 500 миллисекунд static int count=0; if(count<500) { count+=TIMER_STEP_MSC; return; } //--- count=0; m_status_bar.ValueToItem(1,TimeToString(TimeLocal(),TIME_DATE|TIME_SECONDS)); m_chart.Redraw(); }
Если всё сделали правильно, то результат должен быть таким, как на скриншоте ниже:
Рис. 2. Тест элемента «Статусная строка».
Разработка класса для создания элемента «Статусная строка» завершена. Полную версию можно посмотреть в приложенных к статье файлах.
Элемент «Всплывающая подсказка»
Далее займёмся разработкой класса для создания всплывающих подсказок. Ранее, в четвёртой главе первой части серии, при рассмотрении функций для кнопок формы, довольно подробно излагалось, почему для этого элемента нужно сделать отдельный класс. Если изложить это очень кратко, то нужны подсказки без ограничений на количество символов и с возможностью выделять некоторые слова. Реализуем это, используя класс для рисования элементов. Ранее уже был показан пример, как рисовать элементы интерфейса. Во второй главе второй части этой серии статей был создан класс CSeparateLine для создания элемента «Разделительная линия». Теперь по такому же принципу напишем класс для создания всплывающих подсказок.
Рис. 3. Пример всплывающей подсказки в программе Word.
Создайте файл Tooltip.mqh и подключите его к файлу WndContainer.mqh, к которому подключаются все элементы интерфейса. Далее создайте в этом новом файле класс с уже знакомыми стандартными для всех элементов интерфейса методами. Кроме указателя на форму, в этом классе понадобится также указатель на элемент, к которому будет привязываться всплывающая подсказка, а также метод, с помощью которого можно этот указатель сохранить.
//+------------------------------------------------------------------+ //| Tooltip.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" //+------------------------------------------------------------------+ //| Класс для создания всплывающей подсказки | //+------------------------------------------------------------------+ class CTooltip : public CElement { private: //--- Указатель на форму, к которой элемент присоединён CWindow *m_wnd; //--- Указатель на элемент, к которому присоединена всплывающая подсказка CElement *m_element; //--- public: //--- (1) Сохраняет указатель формы, (2) сохраняет указатель элемента void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } void ElementPointer(CElement &object) { m_element=::GetPointer(object); } //--- public: //--- Обработчик событий графика virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Перемещение элемента virtual void Moving(const int x,const int y); //--- (1) Показ, (2) скрытие, (3) сброс, (4) удаление virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTooltip::CTooltip(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTooltip::~CTooltip(void) { }
Свойства, которые может задать пользователь перед созданием всплывающей подсказки:
- Заголовок
- Массив строк
Остальные свойства сделаем со значениями по умолчанию. К ним относятся:
- Цвета градиента фона подсказки
- Цвет рамки фона
- Цвет заголовка
- Цвет текста
- Альфа-канал для управления прозрачностью подсказки
Массив строк можно добавить построчно перед созданием всплывающей подсказки с помощью метода CTooltip::AddString().
class CTooltip : public CElement { private: //--- Свойства: // Заголовок string m_header; //--- Массив строк текста подсказки string m_tooltip_lines[]; //--- Значение альфа-канала (прозрачность подсказки) uchar m_alpha; //--- Цвета (1) текста, (2) заголовка и (3) рамки фона color m_text_color; color m_header_color; color m_border_color; //--- Массив градиента фона color m_array_color[]; //--- public: //--- Заголовок всплывающей подсказки void Header(const string text) { m_header=text; } //--- Добавляет строку для подсказки void AddString(const string text); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTooltip::CTooltip(void) : m_header(""), m_alpha(0), m_text_color(clrDimGray), m_header_color(C'50,50,50'), m_border_color(C'118,118,118'), m_gradient_top_color(clrWhite), m_gradient_bottom_color(C'208,208,235') { //--- Сохраним имя класса элемента в базовом классе CElement::ClassName(CLASS_NAME); } //+------------------------------------------------------------------+ //| Добавляет строку | //+------------------------------------------------------------------+ void CTooltip::AddString(const string text) { //--- Увеличим размер массивов на один элемент int array_size=::ArraySize(m_tooltip_lines); ::ArrayResize(m_tooltip_lines,array_size+1); //--- Сохраним значения переданных параметров m_tooltip_lines[array_size]=text; }
Так же, как и в классе элемента «Разделительная линия», для создания всплывающей подсказки будет использоваться класс CRectCanvas, а также два метода (публичный и приватный).
class CTooltip : public CElement { private: //--- Объекты для создания всплывающей подсказки CRectCanvas m_canvas; //--- public: //--- Методы для создания всплывающей подсказки bool CreateTooltip (const long chart_id,const int subwin); //--- private: //--- Создаёт холст для рисования подсказки bool CreateCanvas(void); };
В публичном методе CTooltip::CreateTooltip(), кроме проверки на наличие указателя на форму, к которой будет присоединён элемент, есть ещё аналогичная проверка на наличие указателя на элемент, для которого эта подсказка предназначена. При наличии указателя на элемент, координаты для всплывающей подсказки рассчитываются относительно координат этого элемента. В данном случае — на один пиксель ниже нижней границы элемента.
//+------------------------------------------------------------------+ //| Создаёт объект Tooltip | //+------------------------------------------------------------------+ bool CTooltip::CreateTooltip(const long chart_id,const int subwin) { //--- Выйти, если нет указателя на форму if(::CheckPointer(m_wnd)==POINTER_INVALID) { ::Print(__FUNCTION__," > Перед созданием всплывающей подсказки классу нужно передать " "указатель на форму: CTooltip::WindowPointer(CWindow &object)."); return(false); } //--- Выйти, если нет указателя на элемент if(::CheckPointer(m_element)==POINTER_INVALID) { ::Print(__FUNCTION__," > Перед созданием всплывающей подсказки классу нужно передать " "указатель на элемент: CTooltip::ElementPointer(CElement &object)."); return(false); } //--- Инициализация переменных m_id =m_wnd.LastId()+1; m_chart_id =chart_id; m_subwin =subwin; m_x =m_element.X(); m_y =m_element.Y2()+1; //--- Отступы от крайней точки CElement::XGap(m_x-m_wnd.X()); CElement::YGap(m_y-m_wnd.Y()); //--- Создаёт всплывающую подсказку if(!CreateTooltip()) return(false); //--- return(true); }
В приватном методе создания холста для рисования CTooltip::CreateCanvas(), в отличие от аналогичного метода в классе CSeparateLine, вместо рисования после создания объекта и установки свойств, далее производятся следующие действия:
- Установка размера массива градиента для фона подсказки. Размер массива равен количеству пикселей в параметре высоты (размер по оси Y) объекта
- Инициализация массива градиента
- Очистка холста для рисования. Устанавливается прозрачность на 100%. Значение альфа-канала равно нулю
В самом конце метода объект добавляется в общий массив объектов элемента. В листинге ниже можно подробнее изучить код этого метода.
//+------------------------------------------------------------------+ //| Создаёт холст для рисования | //+------------------------------------------------------------------+ bool CTooltip::CreateCanvas(void) { //--- Формирование имени объекта string name=CElement::ProgramName()+"_help_tooltip_"+(string)CElement::Id(); //--- Создадим холст if(!m_canvas.CreateBitmapLabel(m_chart_id,m_subwin,name,m_x,m_y,m_x_size,m_y_size,COLOR_FORMAT_ARGB_NORMALIZE)) return(false); //--- Прикрепить к графику if(!m_canvas.Attach(m_chart_id,name,m_subwin,1)) return(false); //--- Установим свойства m_canvas.Background(false); //--- Отступы от крайней точки m_canvas.XGap(m_x-m_wnd.X()); m_canvas.YGap(m_y-m_wnd.Y()); //--- Установка размера массива градиента для фона подсказки CElement::GradientColorsTotal(m_y_size); ::ArrayResize(m_array_color,m_y_size); //--- Инициализация массива градиента CElement::InitColorArray(m_gradient_top_color,m_gradient_bottom_color,m_array_color); //--- Очистка холста для рисования m_canvas.Erase(::ColorToARGB(clrNONE,0)); m_canvas.Update(); m_alpha=0; //--- Сохраним указатель объекта CElement::AddToArray(m_canvas); return(true); }
Далее рассмотрим методы для рисования вертикального градиента и рамки. Для градиента уже есть массив, инициализированный при создании холста нужными значениями. Поэтому в цикле, количество итераций которого равно высоте холста, просто рисуем линию за линией с указанным из массива цветом. Для рисования рамки в самом начале метода объявляются и тут же инициализируются массивы с координатами для каждой стороны холста. После того, как рамка нарисована, производится округление углов на один пиксель и затем дорисовываются ещё четыре пикселя по четырём углам внутрь холста.
В обоих методах есть только один параметр – альфа-канал. То есть, вызывая эти методы можно указать, с какой степенью прозрачности нужно нарисовать градиент и рамку.
//+------------------------------------------------------------------+ //| Вертикальный градиент | //+------------------------------------------------------------------+ void CTooltip::VerticalGradient(const uchar alpha) { //--- Координаты X int x1=0; int x2=m_x_size; //--- Рисуем градиент for(int y=0; y<m_y_size; y++) m_canvas.Line(x1,y,x2,y,::ColorToARGB(m_array_color[y],alpha)); } //+------------------------------------------------------------------+ //| Рамка | //+------------------------------------------------------------------+ void CTooltip::Border(const uchar alpha) { //--- Цвет рамки color clr=m_border_color; //--- Границы int x_size =m_canvas.X_Size()-1; int y_size =m_canvas.Y_Size()-1; //--- Координаты: Сверху/Справа/Снизу/Слева int x1[4]; x1[0]=0; x1[1]=x_size; x1[2]=0; x1[3]=0; int y1[4]; y1[0]=0; y1[1]=0; y1[2]=y_size; y1[3]=0; int x2[4]; x2[0]=x_size; x2[1]=x_size; x2[2]=x_size; x2[3]=0; int y2[4]; y2[0]=0; y2[1]=y_size; y2[2]=y_size; y2[3]=y_size; //--- Рисуем рамку по указанным координатам for(int i=0; i<4; i++) m_canvas.Line(x1[i],y1[i],x2[i],y2[i],::ColorToARGB(clr,alpha)); //--- Округление по углам на один пиксель clr=clrBlack; m_canvas.PixelSet(0,0,::ColorToARGB(clr,0)); m_canvas.PixelSet(0,m_y_size-1,::ColorToARGB(clr,0)); m_canvas.PixelSet(m_x_size-1,0,::ColorToARGB(clr,0)); m_canvas.PixelSet(m_x_size-1,m_y_size-1,::ColorToARGB(clr,0)); //--- Дорисовка пикселей по указанным координатам clr=C'180,180,180'; m_canvas.PixelSet(1,1,::ColorToARGB(clr,alpha)); m_canvas.PixelSet(1,m_y_size-2,::ColorToARGB(clr,alpha)); m_canvas.PixelSet(m_x_size-2,1,::ColorToARGB(clr,alpha)); m_canvas.PixelSet(m_x_size-2,m_y_size-2,::ColorToARGB(clr,alpha)); }
Сделаем так, чтобы подсказка сразу появлялась при наведении курсора мыши на элемент и плавно исчезала, когда курсор уходит из области элемента.
Для отображения (появления) подсказки создадим метод CTooltip::ShowTooltip(). В самом начале этого метода производится проверка на значение поля m_alpha (альфа-канал). Если установлено, что значение альфа-канала уже равно или выше 255, то это означает, что программа уже сюда заходила и подсказка видна на 100%, поэтому не имеет смысла идти дальше. В противном случае далее устанавливаем координаты и отступ для заголовка подсказки, рисуем градиент и рамку. Заголовок не будет нарисован, если пользователь не указал текст для него. Если текст для заголовка есть, то устанавливаются параметры для шрифта и рисуется заголовок.
После этого определяются координаты для основного текста подсказки с учётом наличия заголовка. Устанавливаются параметры уже для основного текста. В отличие от заголовка, текст которого выделен жирным шрифтом (FW_BLACK), у основного текста шрифт будет менее броский (FW_THIN). Затем в цикле из инициализированного пользователем массива на холст выводится текст подсказки. Координата по оси Y для каждой строки корректируется на указанное значение в каждой итерации. В самом конце холст обновляется для отображения изменений и устанавливается признак полностью видимой подсказки. В листинге ниже вы можете подробнее ознакомиться с кодом этого метода.
//+------------------------------------------------------------------+ //| Отображает всплывающую подсказку | //+------------------------------------------------------------------+ void CTooltip::ShowTooltip(void) { //--- Выйти, если подсказка видна на 100% if(m_alpha>=255) return; //--- Координаты и отступ для заголовка int x =5; int y =5; int y_offset =15; //--- Рисуем градиент VerticalGradient(255); //--- Рисуем рамку Border(255); //--- Рисуем заголовок (если установлен) if(m_header!="") { //--- Установим параметры шрифта m_canvas.FontSet(FONT,-80,FW_BLACK); //--- Рисуем текст заголовка m_canvas.TextOut(x,y,m_header,::ColorToARGB(m_header_color),TA_LEFT|TA_TOP); } //--- Координаты для основного текста подсказки (с учётом наличия заголовка) x=(m_header!="")? 15 : 5; y=(m_header!="")? 25 : 5; //--- Установим параметры шрифта m_canvas.FontSet(FONT,-80,FW_THIN); //--- Рисуем основной текст подсказки int lines_total=::ArraySize(m_tooltip_lines); for(int i=0; i<lines_total; i++) { m_canvas.TextOut(x,y,m_tooltip_lines[i],::ColorToARGB(m_text_color),TA_LEFT|TA_TOP); y=y+y_offset; } //--- Обновить холст m_canvas.Update(); //--- Признак полностью видимой подсказки m_alpha=255; }
Для плавного исчезновения подсказки напишем метод CTooltip::FadeOutTooltip(). В самом начале этого метода производится обратная проверка на значение альфа-канала. То есть, если мы уже достигли полной прозрачности, то дальше идти не нужно. В противном случае в цикле с указанным шагом на убывание холст перерисовывается, пока не будет достигнута полная прозрачность. В листинге ниже представлена полная версия этого метода.
//+------------------------------------------------------------------+ //| Плавное исчезновение всплывающей подсказки | //+------------------------------------------------------------------+ void CTooltip::FadeOutTooltip(void) { //--- Выйти, если подсказка скрыта на 100% if(m_alpha<1) return; //--- Отступ для заголовка int y_offset=15; //--- Шаг прозрачности uchar fadeout_step=7; //--- Плавное исчезновение подсказки for(uchar a=m_alpha; a>=0; a-=fadeout_step) { //--- Если следующий шаг в минус, остановим цикл if(a-fadeout_step<0) { a=0; m_canvas.Erase(::ColorToARGB(clrNONE,0)); m_canvas.Update(); m_alpha=0; break; } //--- Координаты для заголовка int x =5; int y =5; //--- Рисуем градиент и рамку VerticalGradient(a); Border(a); //--- Рисуем заголовок (если установлен) if(m_header!="") { //--- Установим параметры шрифта m_canvas.FontSet(FONT,-80,FW_BLACK); //--- Рисуем текст заголовка m_canvas.TextOut(x,y,m_header,::ColorToARGB(m_header_color,a),TA_LEFT|TA_TOP); } //--- Координаты для основного текста подсказки (с учётом наличия заголовка) x=(m_header!="")? 15 : 5; y=(m_header!="")? 25 : 5; //--- Установим параметры шрифта m_canvas.FontSet(FONT,-80,FW_THIN); //--- Рисуем основной текст подсказки int lines_total=::ArraySize(m_tooltip_lines); for(int i=0; i<lines_total; i++) { m_canvas.TextOut(x,y,m_tooltip_lines[i],::ColorToARGB(m_text_color,a),TA_LEFT|TA_TOP); y=y+y_offset; } //--- Обновить холст m_canvas.Update(); } }
Все методы для создания и рисования подсказки готовы. Вызывать их нужно в обработчике событий элемента. Чтобы узнать, нажата ли сейчас на форме кнопка для отображения подсказок, в классе CWindow нужно создать метод CWindow::TooltipBmpState():
class CWindow : public CElement { public: //--- Проверка режима показа всплывающих подсказок bool TooltipBmpState(void) const { return(m_button_tooltip.State()); } };
Теперь, если режим для отображения подсказок включен, в случае, когда фокус на элементе, пользователю будет показываться подсказка. Если же курсор мыши находится вне области элемента или форма заблокирована, то подсказка будет скрываться.
//+------------------------------------------------------------------+ //| Обработчик событий графика | //+------------------------------------------------------------------+ void CTooltip::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Обработка события перемещения курсора if(id==CHARTEVENT_MOUSE_MOVE) { //--- Выйти, если элемент скрыт if(!CElement::IsVisible()) return; //--- Выйти, если кнопка всплывающих подсказок на форме отключена if(!m_wnd.TooltipBmpState()) return; //--- Если форма заблокирована if(m_wnd.IsLocked()) { //--- Скрыть подсказку FadeOutTooltip(); return; } //--- Если есть фокус на элементе if(m_element.MouseFocus()) //--- Показать подсказку ShowTooltip(); //--- Если нет фокуса else //--- Скрыть подсказку FadeOutTooltip(); //--- return; } }
Тест всплывающих подсказок
Теперь можно протестировать, как всё это работает. На форме в тестовом эксперте сейчас пять кнопок. Сделаем для каждой кнопки всплывающую подсказку. Объявляем пять экземпляров классов типа CTooltip и пять методов. Для примера приведём имплементацию только одного из них.
class CProgram : public CWndEvents { private: CTooltip m_tooltip1; CTooltip m_tooltip2; CTooltip m_tooltip3; CTooltip m_tooltip4; CTooltip m_tooltip5; //--- private: bool CreateTooltip1(void); bool CreateTooltip2(void); bool CreateTooltip3(void); bool CreateTooltip4(void); bool CreateTooltip5(void); }; //+------------------------------------------------------------------+ //| Создаёт всплывающую подсказку 5 | //+------------------------------------------------------------------+ bool CProgram::CreateTooltip5(void) { #define TOOLTIP5_LINES_TOTAL 3 //--- Сохраним указатель на окно m_tooltip5.WindowPointer(m_window1); //--- Сохраним указатель на элемент m_tooltip5.ElementPointer(m_icon_button5); //--- Массив с текстом подсказки string text[]= { "Элемент управления \"Кнопка с картинкой\" (5).", "Это вторая строка подсказки.", "Это третья строка подсказки." }; //--- Установим свойства перед созданием m_tooltip5.Header("Icon Button 5"); m_tooltip5.XSize(250); m_tooltip5.YSize(80); //--- Добавим текст построчно for(int i=0; i<TOOLTIP5_LINES_TOTAL; i++) m_tooltip5.AddString(text[i]); //--- Создадим элемент управления if(!m_tooltip5.CreateTooltip(m_chart_id,m_subwin)) return(false); //--- Добавим объект в общий массив групп объектов CWndContainer::AddToElementsArray(0,m_tooltip5); return(true); }
В главном методе создания графического интерфейса создание всплывающих подсказок нужно осуществлять в последнюю очередь, чтобы они оказались в самом конце в массиве элементов в базе. Таким образом, они всегда будут располагаться выше других объектов на графике.
//+------------------------------------------------------------------+ //| Создаёт торговую панель | //+------------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- Создание формы 1 для элементов управления //--- Создание элементов управления: // Главное меню //--- Контекстные меню //--- Создание статусной строки //--- Кнопки с картинкой //--- Всплывающие подсказки if(!CreateTooltip1()) return(false); if(!CreateTooltip2()) return(false); if(!CreateTooltip3()) return(false); if(!CreateTooltip4()) return(false); if(!CreateTooltip5()) return(false); //--- Перерисовка графика m_chart.Redraw(); return(true); }
Чтобы отображать кнопку подсказок на форме, при её создании нужно использовать метод CWindow::UseTooltipsButton():
//+------------------------------------------------------------------+ //| Создаёт форму 1 для элементов управления | //+------------------------------------------------------------------+ bool CProgram::CreateWindow1(const string caption_text) { //--- Добавим указатель окна в массив окон CWndContainer::AddWindow(m_window1); //--- Координаты int x=1; int y=20; //--- Свойства m_window1.Movable(true); m_window1.XSize(251); m_window1.YSize(200); m_window1.UseTooltipsButton(); m_window1.CaptionBgColor(clrCornflowerBlue); m_window1.CaptionBgColorHover(C'150,190,240'); //--- Создание формы if(!m_window1.CreateWindow(m_chart_id,m_subwin,caption_text,x,y)) return(false); //--- return(true); }
На скриншоте ниже показан итоговый результат:
Рис. 4. Тест всплывающей подсказки.
Выглядит и работает неплохо! Но теперь для этого элемента нужно сделать персональный массив в базе указателей. В дальнейшем при разработке многооконного режима будет показано, в каких случаях это может пригодиться.
Персональный массив для всплывающих подсказок
Итак, переходим в класс CWndContainer и дополняем его новыми членами. В структуру WindowElements нужно добавить персональный массив для всплывающих подсказок. Также создадим (1) метод CWndContainer::TooltipsTotal() для получения количества подсказок и (2) метод CWndContainer::AddTooltipElements() для добавления указателя подсказки в персональный массив.
class CWndContainer { protected: //--- Структура массивов элементов struct WindowElements { //--- Всплывающие подсказки CTooltip *m_tooltips[]; }; //--- Массив массивов элементов для каждого окна WindowElements m_wnd[]; //--- public: //--- Количество всплывающих подсказок int TooltipsTotal(const int window_index); //--- private: //--- Сохраняет указатели на элементы всплывающих подсказок в базу bool AddTooltipElements(const int window_index,CElement &object); }; //+------------------------------------------------------------------+ //| Возвращает кол-во подсказок по указанному индексу окна | //+------------------------------------------------------------------+ int CWndContainer::TooltipsTotal(const int window_index) { if(window_index>=::ArraySize(m_wnd)) { ::Print(PREVENTING_OUT_OF_RANGE); return(WRONG_VALUE); } //--- return(::ArraySize(m_wnd[window_index].m_tooltips)); } //+------------------------------------------------------------------+ //| Сохраняет указатель на подсказку в персональный массив | //+------------------------------------------------------------------+ bool CWndContainer::AddTooltipElements(const int window_index,CElement &object) { //--- Выйдем, если это не всплывающая подсказка if(object.ClassName()!="CTooltip") return(false); //--- Получим указатель на всплывающую подсказку CTooltip *t=::GetPointer(object); //--- Добавим указатель в персональный массив AddToRefArray(t,m_wnd[window_index].m_tooltips); return(true); }
Вызов метода CWndContainer::AddTooltipElements() нужно осуществлять в публичном методе CWndContainer::AddToElementsArray(), который используется в пользовательском классе при добавлении элемента в базу. В листинге кода ниже показана его сокращённая версия.
//+------------------------------------------------------------------+ //| Добавляет указатель в массив элементов | //+------------------------------------------------------------------+ void CWndContainer::AddToElementsArray(const int window_index,CElement &object) { //--- Если в базе нет форм для элементов управления //--- Если запрос на несуществующую форму //--- Добавим в общий массив элементов //--- Добавим объекты элемента в общий массив объектов //--- Запомним во всех формах id последнего элемента //--- Увеличим счётчик идентификаторов элементов //--- Сохраняет указатели на объекты контекстного меню в базу //--- Сохраняет указатели на объекты главного меню в базу //--- Сохраняет указатели на объекты сдвоенной кнопки в базу //--- Сохраняет указатели на объекты всплывающей подсказки в базу if(AddTooltipElements(window_index,object)) return; }
Разработка класса для создания всплывающих подсказок завершена. Полную версию Вы можете загрузить к себе на компьютер в конце статьи.
Заключение
В этой статье (первой главе четвёртой части) мы рассмотрели разработку информационных элементов интерфейса, таких как «Строка состояния» и «Всплывающая подсказка». Во второй главе четвёртой части серии реализуем возможность создавать многооконные графические интерфейсы, а также будет затронута такая тема, как система управления приоритетами на нажатие левой кнопкой мыши.
Ниже вы можете загрузить к себе на компьютер весь материал первой части серии, чтобы у Вас сразу была возможность протестировать, как всё это работает. Если у Вас возникают вопросы по использованию материала предоставленного в этих файлах, то Вы можете обратиться к подробному описанию процесса разработки библиотеки в одной из статей в представленном списке ниже или задать вопрос в комментариях к статье.
Список статей (глав) четвёртой части:
- Графические интерфейсы IV: Информационные элементы интерфейса (Глава 1)
- Графические интерфейсы IV: Многооконный режим и система приоритетов (Глава 2)





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
В этой версии библиотеки нет класса CElementBase
После имени объекта в коде поставьте точку и начните набирать fo - интеллиенс вам и выдаст список доступных методов.
Нет необходимых методов
Нет необходимых методов
Последняя версия библиотеки в этой статье: Графические интерфейсы X: Выделение текста в многострочном поле ввода (build 13)
Последняя версия библиотеки в этой статье: Графические интерфейсы X: Выделение текста в многострочном поле ввода (build 13)
спасибо
этот кодекс является такой веб - сайт
Но это загрузить исходный код