//+------------------------------------------------------------------+ //| Controls.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ #define DEF_BUTTON_WIDTH 60 // ширина кнопки по умолчанию //+------------------------------------------------------------------+ //| includes | //+------------------------------------------------------------------+ #include #include enum ENUM_OBJECT_TYPE // Перечисление типов объектов { OBJECT_TYPE_BASE=1, // Базовый объект рисования на канвасе OBJECT_TYPE_PANEL, // Простая панель OBJECT_TYPE_LABEL, // Текстовая метка OBJECT_TYPE_BUTTON, // Простая кнопка OBJECT_TYPE_TBUTTON, // Двухпозиционная кнопка (вкл/выкл) OBJECT_TYPE_SBUTTON, // Кнопка-переключатель OBJECT_TYPE_TAB_BUTTON, // Кнопка вкладки OBJECT_TYPE_TAB, // Вкладка OBJECT_TYPE_TAB_CONTROL, // Набор вкладок }; //+------------------------------------------------------------------+ //| Базовый класс рисования | //+------------------------------------------------------------------+ class CBaseCanvas : public CObject { private: bool m_base_obj; // Признак базового объекта в иерархии связанных объектов bool m_blocked; // Признак заблокированного объекта int m_id; // Идентификатор объекта protected: CCanvas m_canvas; // Канвас CRect m_bound; // Границы объекта, края и координаты long m_chart_id; // Идентификатор графика uchar m_alpha; // Значение прозрачности холста ushort m_text_obj[]; // Массив символов текста объекта public: CCanvas *GetCanvas(void) { return &this.m_canvas; } virtual int Type(void) const { return(OBJECT_TYPE_BASE); } void SetBaseObjFlag(const bool flag) { this.m_base_obj=flag; } bool IsBaseObject(void) const { return this.m_base_obj; } bool IsBlocked(void) const { return this.m_blocked; } void SetChartID(const long chart_id) { this.m_chart_id=chart_id; } long ChartID(void) const { return this.m_chart_id; } void SetID(const int id) { this.m_id=id; } int ID(void) const { return this.m_id; } int X(void) const { return this.m_bound.left; } int Y(void) const { return this.m_bound.top; } int Width(void) const { return this.m_canvas.Width(); } int Height(void) const { return this.m_canvas.Height(); } int Right(void) const { return this.m_bound.right; } int Bottom(void) const { return this.m_bound.bottom; } string Text(void) const { return ::ShortArrayToString(this.m_text_obj); } string Name(void) const { return this.m_canvas.ChartObjectName(); } //--- Все виртуальные методы могут переопределяться в наследуемых классах //--- Метод рисования внешнего вида объекта virtual void Draw(const bool chart_redraw) { return; } //--- Очистка всего объекта - заливка полностью прозрачным цветом virtual void Clear(void) { this.m_canvas.Erase(0x00FFFFFF); } //--- Обновление канваса для отображения проведённых на холсте изменений virtual void UpdateView(const bool chart_redraw) { this.m_canvas.Update(chart_redraw); } //--- Создание графического объекта и присоединение к нему ресурса холста virtual bool Create(const string name, const string object_text, const int x, const int y, const int w, const int h); //--- Установка текста, содержащегося в объекте virtual bool SetTextObject(const string text) { return(::StringToShortArray(text, this.m_text_obj)==text.Length()); } //--- Скрытие и отображение объекта на графике virtual bool Hide(void) const { return ::ObjectSetInteger(this.m_chart_id, this.Name(), OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); } virtual bool Show(void) const { return ::ObjectSetInteger(this.m_chart_id, this.Name(), OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS); } //--- Перенос объекта на передний план virtual bool BringToTop(void) const { bool res=this.Hide(); res &=this.Show(); return res;} //--- Возврат флага скрытого объекта bool IsHidden(void) const { return(::ObjectGetInteger(this.m_chart_id, this.Name(), OBJPROP_TIMEFRAMES)==OBJ_NO_PERIODS); } //--- Установка блокированного/разблокированного состояний объекта virtual void SetBlocked(void) { this.m_blocked=true; } virtual void SetUnblocked(void) { this.m_blocked=false; } //--- Изменение размеров virtual bool Resize(const int w, const int h); //--- Перемещение virtual bool MoveX(const int x); virtual bool MoveY(const int y); virtual bool Move(const int x, const int y); //--- Конструкторы/деструктор CBaseCanvas(void) : m_chart_id(::ChartID()), m_id(0), m_base_obj(false), m_alpha(255) {} CBaseCanvas(const long chart_id) : m_chart_id(chart_id), m_id(0), m_base_obj(false), m_alpha(255) {} ~CBaseCanvas(void){ this.m_canvas.Destroy(); } }; //+------------------------------------------------------------------+ //| Создаёт базовый объект канвас | //+------------------------------------------------------------------+ bool CBaseCanvas::Create(const string name, const string object_text, const int x, const int y, const int w, const int h) { //--- создаём объект OBJ_BITMAP_LABEL и его графический ресурс (ширина и высота не менее 1 пикселя) if(!this.m_canvas.CreateBitmapLabel(this.m_chart_id, 0, name, x, y, (w<1 ? 1 : w), (h<1 ? 1 : h), COLOR_FORMAT_ARGB_NORMALIZE)) { ::PrintFormat("%s: CreateBitmapLabel() failed. x=%d, y=%d, w=%d, h=%d",__FUNCTION__, x, y, w, h); return false; } //--- устанавливаем координаты и размеры ограничивающего прямоугольника и записываем текст, содержащийся в объекте this.m_bound.LeftTop(x, y); this.m_bound.Width(w); this.m_bound.Height(h); this.SetTextObject(object_text); return true; } //+------------------------------------------------------------------+ //| Изменяет размеры холста | //+------------------------------------------------------------------+ bool CBaseCanvas::Resize(const int w,const int h) { //--- если переданы уже установленные значения размеров - возвращаем true if(w==this.Width() && h==this.Height()) return true; //--- изменяем размеры графического объекта if(!this.m_canvas.Resize(w, h)) return false; //--- устанавливаем новые размеры очерчивающему прямоугольнику и возвращаем true this.m_bound.Width(w); this.m_bound.Height(h); return true; } //+------------------------------------------------------------------+ //| Перемещает объект на новую координату x | //+------------------------------------------------------------------+ bool CBaseCanvas::MoveX(const int x) { //--- если передано уже установленное значение координаты - возвращаем true if(x==this.X()) return true; //--- изменяем координату графического объекта if(!::ObjectSetInteger(this.m_chart_id, this.Name(), OBJPROP_XDISTANCE, x)) return false; //--- устанавливаем новое значение очерчивающему прямоугольнику и возвращаем true this.m_bound.Move(x, this.Y()); return true; } //+------------------------------------------------------------------+ //| Перемещает объект на новую координату y | //+------------------------------------------------------------------+ bool CBaseCanvas::MoveY(const int y) { //--- если передано уже установленное значение координаты - возвращаем true if(y==this.Y()) return true; //--- изменяем координату графического объекта if(!::ObjectSetInteger(this.m_chart_id, this.Name(), OBJPROP_YDISTANCE, y)) return false; //--- устанавливаем новое значение очерчивающему прямоугольнику и возвращаем true this.m_bound.Move(this.X(), y); return true; } //+------------------------------------------------------------------+ //| Перемещает объект на новые координаты x, y | //+------------------------------------------------------------------+ bool CBaseCanvas::Move(const int x,const int y) { //--- возвращаем результат изменения обеих координат объекта if(!this.MoveX(x)) return false; return this.MoveY(y); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| enums | //+------------------------------------------------------------------+ enum ENUM_COLOR // Список элементов объекта с изменяемым цветом { COLOR_BACKGROUND, // Фон COLOR_FOREGROUND, // Текст COLOR_BORDER, // Рамка }; #define NUM_COLOR_ELEMENTS 3 // Количество элементов объекта с изменяемым цветом enum ENUM_REASON_COLOR // Список причин изменения цвета { REASON_COLOR_STANDARD, // Объект в обычном состоянии REASON_COLOR_FOCUSED, // При наведении курсора REASON_COLOR_PRESSED, // При нажатии REASON_COLOR_BLOCKED, // Заблокирован }; #define NUM_REASON_COLORS 4 // Количество цветов различных состояний enum ENUM_STATE // Состояния кнопки { STATE_OFF, // Отжата STATE_ON, // Нажата }; #define NUM_STATES 2 // Количество состояний кнопок enum ENUM_REFERENCE_OBJ // Эталонный объект для контроля размеров { REFERENCE_OBJ_NONE, // Нет REFERENCE_OBJ_CHART, // График REFERENCE_OBJ_OBJ, // Иной объект }; //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Класс панели | //+------------------------------------------------------------------+ class CPanel : public CBaseCanvas { private: bool m_chart_mouse_wheel; // Флаг отправки сообщений о прокрутке колёсика мышки bool m_chart_mouse_move; // Флаг отправки сообщений о перемещениях курсора мышки bool m_chart_context_menu; // Флаг доступа к контекстному меню по нажатию правой клавиши мышки bool m_chart_crosshair_tool; // Флаг доступа к инструменту "Перекрестие" по нажатию средней клавиши мышки ENUM_STATE m_state; // Состояние элемента (нажат, отжат и т.д.) //--- Установка разрешений для событий мышки на графике void MouseEventSet(void); protected: CArrayObj m_list_obj; // Список присоединённых дочерних объектов bool m_resize; // Флаг возможности менять размеры ENUM_REFERENCE_OBJ m_reference_obj; // Объект для контроля изменения размеров color m_array_default_color[NUM_COLOR_ELEMENTS][NUM_REASON_COLORS][NUM_STATES]; // Массив предустановленных цветов различных состояний объекта color m_array_used_color[NUM_COLOR_ELEMENTS][NUM_REASON_COLORS][NUM_STATES]; // Массив цветов различных состояний объекта //--- Установка запретов для графика void ChartToolsSet(const bool flag); //--- Инициализация объекта класса void Init(void); //--- Изменяет цвета фона, текста и рамки в зависимости от условия void ColorChange(const ENUM_REASON_COLOR reason); //--- Проверяет установленный цвет на равенство указанному bool CheckColor(const ENUM_REASON_COLOR reason) const; //--- Обработчики событий перемещения курсора и нажатий клавиш мышки. Должны определяться в наследниках virtual void MouseMoveHandler(const int id, const long lparam, const double dparam, const string sparam) { return; } // обработчик здесь отключен virtual void MousePressHandler(const int id, const long lparam, const double dparam, const string sparam) { return; } // обработчик здесь отключен virtual void CustomMouseMoveHandler(const int id, const long lparam, const double dparam, const string sparam) { return; } // обработчик здесь отключен virtual void CustomMousePressHandler(const int id, const long lparam, const double dparam, const string sparam) { return; } // обработчик здесь отключен public: //--- Установка и возврат состояния элемента virtual void SetState(const ENUM_STATE state) { this.m_state=state; } ENUM_STATE State(void) const { return this.m_state; } //--- Устанавливает флаг возможности изменения размеров void SetResizeFlag(const bool flag) { this.m_resize=flag; } //--- Тип объекта virtual int Type(void) const { return(OBJECT_TYPE_PANEL); } //--- Возврат списка присоединённых объектов virtual CArrayObj*GetList(void) { return &this.m_list_obj; } //--- Добавление нового объекта в список virtual bool AttachObject(CBaseCanvas *element) { if(element==NULL) return false; element.SetBaseObjFlag(false); return(this.m_list_obj.Add(element)); } //--- Возвращает (1) указатель на присоединённый объект по индексу, (2) количество объектов в списке CObject *GetAttachedObj(const int index) { return this.m_list_obj.At(index); } int AttachedObjTotal(void) const { return this.m_list_obj.Total(); } private: //--- (1) Установка, (2) возврат предустановленного цвета указанного состояния для указанного элемента объекта void SetColorDefault(const ENUM_COLOR color_element, const ENUM_REASON_COLOR reason, const ENUM_STATE state, const color clr) { this.m_array_default_color[color_element][reason][state]=clr; } color GetColorDefault(const ENUM_COLOR color_element, const ENUM_REASON_COLOR reason, const ENUM_STATE state) const { return this.m_array_default_color[color_element][reason][state]; } //--- (1) Установка, (2) возврат цвета указанного состояния для указанного элемента объекта void SetColor(const ENUM_COLOR color_element, const ENUM_REASON_COLOR reason, const ENUM_STATE state, const color clr) { this.m_array_used_color[color_element][reason][state]=clr; } color GetColor(const ENUM_COLOR color_element, const ENUM_REASON_COLOR reason, const ENUM_STATE state) const { return this.m_array_used_color[color_element][reason][state]; } public: //--- Установка предустановленных цветов всех состояний для указанного элемента объекта void SetDefaultColors(const ENUM_COLOR color_element, const ENUM_STATE state, const color standard, const color focused, const color pressed, const color blocked) { this.SetColorDefault(color_element, REASON_COLOR_STANDARD,state, standard); this.SetColorDefault(color_element, REASON_COLOR_FOCUSED, state, focused); this.SetColorDefault(color_element, REASON_COLOR_PRESSED, state, pressed); this.SetColorDefault(color_element, REASON_COLOR_BLOCKED, state, blocked); } //--- Возврат предустановленных цветов фона в различных состояниях color DefaultBackColor(void) const { return this.GetColorDefault(COLOR_BACKGROUND, REASON_COLOR_STANDARD,this.State()); } color DefaultBackColorFocused(void) const { return this.GetColorDefault(COLOR_BACKGROUND, REASON_COLOR_FOCUSED, this.State()); } color DefaultBackColorPressed(void) const { return this.GetColorDefault(COLOR_BACKGROUND, REASON_COLOR_PRESSED, this.State()); } color DefaultBackColorBlocked(void) const { return this.GetColorDefault(COLOR_BACKGROUND, REASON_COLOR_BLOCKED, this.State()); } //--- Возврат предустановленных цветов текста в различных состояниях color DefaultForeColor(void) const { return this.GetColorDefault(COLOR_FOREGROUND, REASON_COLOR_STANDARD,this.State()); } color DefaultForeColorFocused(void) const { return this.GetColorDefault(COLOR_FOREGROUND, REASON_COLOR_FOCUSED, this.State()); } color DefaultForeColorPressed(void) const { return this.GetColorDefault(COLOR_FOREGROUND, REASON_COLOR_PRESSED, this.State()); } color DefaultForeColorBlocked(void) const { return this.GetColorDefault(COLOR_FOREGROUND, REASON_COLOR_BLOCKED, this.State()); } //--- Возврат предустановленных цветов рамки в различных состояниях color DefaultBorderColor(void) const { return this.GetColorDefault(COLOR_BORDER, REASON_COLOR_STANDARD,this.State()); } color DefaultBorderColorFocused(void) const { return this.GetColorDefault(COLOR_BORDER, REASON_COLOR_FOCUSED, this.State()); } color DefaultBorderColorPressed(void) const { return this.GetColorDefault(COLOR_BORDER, REASON_COLOR_PRESSED, this.State()); } color DefaultBorderColorBlocked(void) const { return this.GetColorDefault(COLOR_BORDER, REASON_COLOR_BLOCKED, this.State()); } //--- Установка цветов всех состояний для указанного элемента объекта void SetColors(const ENUM_COLOR color_element, const ENUM_STATE state, const color standard, const color focused, const color pressed, const color blocked) { this.SetColor(color_element, REASON_COLOR_STANDARD,state, standard); this.SetColor(color_element, REASON_COLOR_FOCUSED, state, focused); this.SetColor(color_element, REASON_COLOR_PRESSED, state, pressed); this.SetColor(color_element, REASON_COLOR_BLOCKED, state, blocked); } //--- Установка цветов фона для различных состояний void SetBackColor(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BACKGROUND, REASON_COLOR_STANDARD,state, clr); } void SetBackColorFocused(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BACKGROUND, REASON_COLOR_FOCUSED, state, clr); } void SetBackColorPressed(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BACKGROUND, REASON_COLOR_PRESSED, state, clr); } void SetBackColorBlocked(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BACKGROUND, REASON_COLOR_BLOCKED, state, clr); } //--- Установка цветов текста для различных состояний void SetForeColor(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_FOREGROUND, REASON_COLOR_STANDARD,state, clr); } void SetForeColorFocused(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_FOREGROUND, REASON_COLOR_FOCUSED, state, clr); } void SetForeColorPressed(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_FOREGROUND, REASON_COLOR_PRESSED, state, clr); } void SetForeColorBlocked(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_FOREGROUND, REASON_COLOR_BLOCKED, state, clr); } //--- Установка цветов рамки для различных состояний void SetBorderColor(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BORDER, REASON_COLOR_STANDARD,state, clr); } void SetBorderColorFocused(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BORDER, REASON_COLOR_FOCUSED, state, clr); } void SetBorderColorPressed(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BORDER, REASON_COLOR_PRESSED, state, clr); } void SetBorderColorBlocked(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BORDER, REASON_COLOR_BLOCKED, state, clr); } //--- Возврат цветов фона в различных состояниях color BackColor(void) const { return this.GetColor(COLOR_BACKGROUND, REASON_COLOR_STANDARD,this.State()); } color BackColorFocused(void) const { return this.GetColor(COLOR_BACKGROUND, REASON_COLOR_FOCUSED, this.State()); } color BackColorPressed(void) const { return this.GetColor(COLOR_BACKGROUND, REASON_COLOR_PRESSED, this.State()); } color BackColorBlocked(void) const { return this.GetColor(COLOR_BACKGROUND, REASON_COLOR_BLOCKED, this.State()); } //--- Возврат цветов текста в различных состояниях color ForeColor(void) const { return this.GetColor(COLOR_FOREGROUND, REASON_COLOR_STANDARD,this.State()); } color ForeColorFocused(void) const { return this.GetColor(COLOR_FOREGROUND, REASON_COLOR_FOCUSED, this.State()); } color ForeColorPressed(void) const { return this.GetColor(COLOR_FOREGROUND, REASON_COLOR_PRESSED, this.State()); } color ForeColorBlocked(void) const { return this.GetColor(COLOR_FOREGROUND, REASON_COLOR_BLOCKED, this.State()); } //--- Возврат цветов рамки в различных состояниях color BorderColor(void) const { return this.GetColor(COLOR_BORDER, REASON_COLOR_STANDARD,this.State()); } color BorderColorFocused(void) const { return this.GetColor(COLOR_BORDER, REASON_COLOR_FOCUSED, this.State()); } color BorderColorPressed(void) const { return this.GetColor(COLOR_BORDER, REASON_COLOR_PRESSED, this.State()); } color BorderColorBlocked(void) const { return this.GetColor(COLOR_BORDER, REASON_COLOR_BLOCKED, this.State()); } //--- Восстановление всех цветов в DEFAULT значения void ResetUsedColors(const ENUM_STATE state) { this.SetBackColor(state, this.DefaultBackColor()); this.SetBackColorFocused(state, this.DefaultBackColorFocused()); this.SetBackColorPressed(state, this.DefaultBackColorPressed()); this.SetBackColorBlocked(state, this.DefaultBackColorBlocked()); this.SetForeColor(state, this.DefaultForeColor()); this.SetForeColorFocused(state, this.DefaultForeColorFocused()); this.SetForeColorPressed(state, this.DefaultForeColorPressed()); this.SetForeColorBlocked(state, this.DefaultForeColorBlocked()); this.SetBorderColor(state, this.DefaultBorderColor()); this.SetBorderColorFocused(state, this.DefaultBorderColorFocused()); this.SetBorderColorPressed(state, this.DefaultBorderColorPressed()); this.SetBorderColorBlocked(state, this.DefaultBorderColorBlocked()); } //--- Переопределяемые виртуальные методы родительского класса virtual void InitDefaultColors(void); virtual void Draw(const bool chart_redraw) override; virtual void UpdateView(const bool chart_redraw) override; virtual bool Create(const string name, const string object_text, const int x, const int y, const int w, const int h) override; virtual bool Resize(const int w, const int h) override; virtual bool Hide(void) override const; virtual bool Show(void) override const; virtual bool BringToTop(void); virtual void SetBlocked(void); virtual void SetUnblocked(void); //--- Обработчик событий void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Конструкторы/деструктор CPanel(void) : CBaseCanvas(::ChartID()), m_state(STATE_OFF), m_resize(true), m_reference_obj(REFERENCE_OBJ_NONE) { this.Init(); } CPanel(const long chart_id) : CBaseCanvas(chart_id), m_state(STATE_OFF), m_resize(true), m_reference_obj(REFERENCE_OBJ_NONE) { this.Init(); } ~CPanel(void); }; //+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CPanel::~CPanel(void) { //--- Возвращаем разрешения для мышки и инструментов графика ::ChartSetInteger(this.m_chart_id, CHART_EVENT_MOUSE_WHEEL, this.m_chart_mouse_wheel); ::ChartSetInteger(this.m_chart_id, CHART_EVENT_MOUSE_MOVE, this.m_chart_mouse_move); ::ChartSetInteger(this.m_chart_id, CHART_CONTEXT_MENU, this.m_chart_context_menu); ::ChartSetInteger(this.m_chart_id, CHART_CROSSHAIR_TOOL, this.m_chart_crosshair_tool); } //+------------------------------------------------------------------+ //| Инициализация класса | //+------------------------------------------------------------------+ void CPanel::Init(void) { //--- Запоминаем разрешения для мышки и инструментов графика this.m_chart_mouse_wheel=::ChartGetInteger(this.m_chart_id, CHART_EVENT_MOUSE_WHEEL); this.m_chart_mouse_move=::ChartGetInteger(this.m_chart_id, CHART_EVENT_MOUSE_MOVE); this.m_chart_context_menu=::ChartGetInteger(this.m_chart_id, CHART_CONTEXT_MENU); this.m_chart_crosshair_tool=::ChartGetInteger(this.m_chart_id, CHART_CROSSHAIR_TOOL); //--- Устанавливаем разрешения для мышки, цвета по умолчанию и параметры шрифта this.MouseEventSet(); this.InitDefaultColors(); this.ResetUsedColors(STATE_OFF); this.ResetUsedColors(STATE_ON); this.m_canvas.FontSet("Calibri",-100); } //+------------------------------------------------------------------+ //| Установка разрешений на отправку сообщений о событиях мышки | //+------------------------------------------------------------------+ void CPanel::MouseEventSet(void) { if(!this.m_chart_mouse_wheel) ::ChartSetInteger(this.m_chart_id, CHART_EVENT_MOUSE_WHEEL, true); if(!this.m_chart_mouse_move) ::ChartSetInteger(this.m_chart_id, CHART_EVENT_MOUSE_MOVE, true); } //+------------------------------------------------------------------+ //| Установка запретов для графика (контекстное меню и перекрестие) | //+------------------------------------------------------------------+ void CPanel::ChartToolsSet(const bool flag) { //--- Флаг разрешения изменяем только в случае, //--- если у объекта он имеет противоположное значение if(flag) { if(!this.m_chart_context_menu) ::ChartSetInteger(this.m_chart_id, CHART_CONTEXT_MENU, true); if(!this.m_chart_crosshair_tool) ::ChartSetInteger(this.m_chart_id, CHART_CROSSHAIR_TOOL, true); return; } if(this.m_chart_context_menu) ::ChartSetInteger(this.m_chart_id, CHART_CONTEXT_MENU, false); if(this.m_chart_crosshair_tool) ::ChartSetInteger(this.m_chart_id, CHART_CROSSHAIR_TOOL, false); } //+------------------------------------------------------------------+ //| Устанавливает цвета по умолчанию | //+------------------------------------------------------------------+ void CPanel::InitDefaultColors(void) { // standard focused pressed blocked this.SetDefaultColors(COLOR_BACKGROUND, STATE_OFF, clrWhite, clrWhite, clrWhite, clrWhite); this.SetDefaultColors(COLOR_FOREGROUND, STATE_OFF, clrDimGray, clrDimGray, clrDimGray, clrDimGray); this.SetDefaultColors(COLOR_BORDER, STATE_OFF, clrDimGray, clrDimGray, clrDimGray, clrDimGray); } //+------------------------------------------------------------------+ //| Смена цвета элементов объекта по событию | //+------------------------------------------------------------------+ void CPanel::ColorChange(const ENUM_REASON_COLOR reason) { //--- В зависимости от события устанавливаем цвета события как основные switch(reason) { case REASON_COLOR_STANDARD : this.ResetUsedColors(this.State()); break; case REASON_COLOR_FOCUSED : this.SetBackColor(this.State(), this.BackColorFocused()); this.SetForeColor(this.State(), this.ForeColorFocused()); this.SetBorderColor(this.State(), this.BorderColorFocused()); break; case REASON_COLOR_PRESSED : this.SetBackColor(this.State(), this.BackColorPressed()); this.SetForeColor(this.State(), this.ForeColorPressed()); this.SetBorderColor(this.State(), this.BorderColorPressed()); break; case REASON_COLOR_BLOCKED : this.SetBackColor(this.State(), this.BackColorBlocked()); this.SetForeColor(this.State(), this.ForeColorBlocked()); this.SetBorderColor(this.State(), this.BorderColorBlocked()); break; default: break; } } //+------------------------------------------------------------------+ //| Проверяет установленный цвет на равенство указанному | //+------------------------------------------------------------------+ bool CPanel::CheckColor(const ENUM_REASON_COLOR reason) const { bool res=true; //--- В зависимости от проверяемого события switch(reason) { //--- проверяем равенство всех STANDARD цветов фона, текста и рамки предустановленным значениям case REASON_COLOR_STANDARD : res &=this.BackColor()==this.DefaultBackColor(); res &=this.ForeColor()==this.DefaultForeColor(); res &=this.BorderColor()==this.DefaultBorderColor(); break; //--- проверяем равенство всех FOCUSED цветов фона, текста и рамки предустановленным значениям case REASON_COLOR_FOCUSED : res &=this.BackColor()==this.BackColorFocused(); res &=this.ForeColor()==this.ForeColorFocused(); res &=this.BorderColor()==this.BorderColorFocused(); break; //--- проверяем равенство всех PRESSED цветов фона, текста и рамки предустановленным значениям case REASON_COLOR_PRESSED : res &=this.BackColor()==this.BackColorPressed(); res &=this.ForeColor()==this.ForeColorPressed(); res &=this.BorderColor()==this.BorderColorPressed(); break; //--- проверяем равенство всех BLOCKED цветов фона, текста и рамки предустановленным значениям case REASON_COLOR_BLOCKED : res &=this.BackColor()==this.BackColorBlocked(); res &=this.ForeColor()==this.ForeColorBlocked(); res &=this.BorderColor()==this.BorderColorBlocked(); break; default: res=false; break; } return res; } //+------------------------------------------------------------------+ //| Рисует внешний вид панели | //+------------------------------------------------------------------+ void CPanel::Draw(const bool chart_redraw) override { //--- Заливаем канвас цветом фона с установленной прозрачностью, рисуем рамку и обновляем канвас this.m_canvas.Erase(::ColorToARGB(this.BackColor(), this.m_alpha)); this.m_canvas.Rectangle(0, 0, this.m_bound.Width()-1, this.m_bound.Height()-1, ::ColorToARGB(this.BorderColor())); this.UpdateView(chart_redraw); } //+------------------------------------------------------------------+ //| Создаёт панель | //+------------------------------------------------------------------+ bool CPanel::Create(const string name, const string object_text,const int x,const int y,const int w,const int h) override { //--- Создаём графический объект и рисуем его внешний вид с перерисовкой графика if(!CBaseCanvas::Create(name, object_text, x, y, w, h)) return false; this.Draw(true); return true; } //+------------------------------------------------------------------+ //| Изменяет размеры холста | //+------------------------------------------------------------------+ bool CPanel::Resize(const int w,const int h) { //--- Если переданы уже установленные размеры, или запрет на изменение - возвращаем true if(!this.m_resize || (w==this.Width() && h==this.Height())) return true; //--- При ошибке изменении размеров возвращаем false if(!this.m_canvas.Resize(w, h)) return false; //--- записываем новые размеры в ограничивающий прямоугольник this.m_bound.Width(w); this.m_bound.Height(h); //--- всё успешно return true; } //+------------------------------------------------------------------+ //| Скрывает объект панели на графике | //+------------------------------------------------------------------+ bool CPanel::Hide(void) override const { //--- Скрываем родительский объект bool res=CBaseCanvas::Hide(); //--- В цикле по всем привязанным объектам скрываем каждый int total=this.m_list_obj.Total(); for(int i=0; iCHARTEVENT_CUSTOM) { //--- собственные события не обрабатываем if(sparam==this.m_canvas.ChartObjectName()) return; //--- приводим пользовательское событие в соответствие со стандартными ENUM_CHART_EVENT chart_event=ENUM_CHART_EVENT(id-CHARTEVENT_CUSTOM); //--- Если щелчок мышки по объекту if(chart_event==CHARTEVENT_CLICK) { this.CustomMousePressHandler(chart_event, lparam, dparam, sparam); } //--- Если перемещение курсора мышки if(chart_event==CHARTEVENT_MOUSE_MOVE) { this.CustomMouseMoveHandler(chart_event, lparam, dparam, sparam); } } } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Класс текстовой метки | //+------------------------------------------------------------------+ class CLabel : public CPanel { public: //--- Переопределяемые виртуальные методы родительского класса virtual int Type(void) const { return(OBJECT_TYPE_LABEL); } virtual CArrayObj*GetList(void) { return NULL; } virtual bool AttachObject(CBaseCanvas *element) { return false; } virtual void InitDefaultColors(void); virtual void Draw(const bool chart_redraw) override; //--- Конструкторы/деструктор CLabel (void) : CPanel(::ChartID()) { this.Init(); this.SetTextObject(""); } CLabel (const long chart_id, const string text) : CPanel(chart_id) { this.Init(); this.SetTextObject(text); } ~CLabel (void){} }; //+------------------------------------------------------------------+ //| Рисует внешний вид текстовой метки | //+------------------------------------------------------------------+ void CLabel::Draw(const bool chart_redraw) override { //--- Заливаем фон прозрачным цветом, выводим текст и обновляем канвас this.Clear(); this.m_canvas.TextOut(0, 0, this.Text(),::ColorToARGB(this.ForeColor(), this.m_alpha)); this.UpdateView(true); } //+------------------------------------------------------------------+ //| Устанавливает цвета по умолчанию | //+------------------------------------------------------------------+ void CLabel::InitDefaultColors(void) { // standard focused pressed blocked this.SetDefaultColors(COLOR_BACKGROUND, STATE_OFF, clrNONE, clrNONE, clrNONE, clrNONE); this.SetDefaultColors(COLOR_FOREGROUND, STATE_OFF, clrBlack, clrBlue, clrDarkBlue, clrSilver); this.SetDefaultColors(COLOR_BORDER, STATE_OFF, clrNONE, clrNONE, clrNONE, clrNONE); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Класс простой кнопки | //+------------------------------------------------------------------+ class CButton : public CLabel { private: //--- Виртуальные обработчики событий virtual void MouseMoveHandler(const int id, const long lparam, const double dparam, const string sparam); virtual void MousePressHandler(const int id, const long lparam, const double dparam, const string sparam); public: //--- Переопределяемые виртуальные методы родительского класса virtual int Type(void) const { return(OBJECT_TYPE_BUTTON); } virtual CArrayObj*GetList(void) { return NULL; } virtual bool AttachObject(CBaseCanvas *element) { return false; } virtual void InitDefaultColors(void); virtual void Draw(const bool chart_redraw) override; //--- Конструкторы/деструктор CButton (void) : CLabel(::ChartID(), "") { this.Init(); } CButton (const long chart_id, const string text) : CLabel(chart_id, text) { this.Init(); } ~CButton (void){} }; //+------------------------------------------------------------------+ //| Рисует внешний вид кнопки | //+------------------------------------------------------------------+ void CButton::Draw(const bool chart_redraw) override { //--- Заливаем фон цветом фона с установленной прозрачностью и рисуем рамку this.m_canvas.Erase(::ColorToARGB(this.BackColor(), this.m_alpha)); this.m_canvas.Rectangle(0, 0, this.m_bound.Width()-1, this.m_bound.Height()-1, ::ColorToARGB(this.BorderColor())); //--- Получаем центральную точку и рассчитываем координаты текста CPoint pt=this.m_bound.CenterPoint(); int x=pt.x-this.m_bound.left; int y=pt.y-this.m_bound.top; //--- Выводим текст с выравниванием по центру и обновляем канвас this.m_canvas.TextOut(x, y, this.Text(), ::ColorToARGB(this.ForeColor(), this.m_alpha), TA_CENTER|TA_VCENTER); CBaseCanvas::UpdateView(chart_redraw); } //+------------------------------------------------------------------+ //| Устанавливает цвета по умолчанию | //+------------------------------------------------------------------+ void CButton::InitDefaultColors(void) { // standard focused pressed blocked this.SetDefaultColors(COLOR_BACKGROUND, STATE_OFF, clrWhite, C'216,230,242', C'192,220,243', clrLightGray); this.SetDefaultColors(COLOR_FOREGROUND, STATE_OFF, clrDimGray, C'0,127,192', C'47,125,180', clrSilver); this.SetDefaultColors(COLOR_BORDER, STATE_OFF, clrSilver, C'192,220,243', C'144,200,246', clrSilver); } //+------------------------------------------------------------------+ //| Обработчик перемещения курсора мышки на объекте | //+------------------------------------------------------------------+ void CButton::MouseMoveHandler(const int id,const long lparam,const double dparam,const string sparam) { //--- Если цвета объекта не соответствуют цветам при наведении курсора if(!this.CheckColor(REASON_COLOR_FOCUSED)) { //--- устанавливаем нужный цвет и перерисовываем внешний вид this.ColorChange(REASON_COLOR_FOCUSED); this.Draw(true); } } //+------------------------------------------------------------------+ //| Обработчик нажатия кнопки мышки на объекте | //+------------------------------------------------------------------+ void CButton::MousePressHandler(const int id,const long lparam,const double dparam,const string sparam) { //--- Если цвета объекта не соответствуют цветам при щелчке на объекте if(!this.CheckColor(REASON_COLOR_PRESSED)) { //--- устанавливаем нужный цвет и перерисовываем внешний вид this.ColorChange(REASON_COLOR_PRESSED); this.Draw(true); } //--- на щелчок любой кнопкой мышки (sparam = 1: ЛКМ, sparam = 2: ПКМ, sparam = 16: СКМ) //--- отправляем пользовательское событие на график с именем объекта, по которому был щелчок в sparam ::EventChartCustom(this.m_chart_id, (ushort)CHARTEVENT_CLICK, lparam, dparam, this.Name()); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Класс двухпозиционной кнопки | //+------------------------------------------------------------------+ class CButtonTriggered : public CButton { private: //--- Виртуальные обработчики событий virtual void MousePressHandler(const int id, const long lparam, const double dparam, const string sparam); public: //--- Переопределяемые виртуальные методы родительского класса virtual int Type(void) const { return(OBJECT_TYPE_TBUTTON); } virtual void InitDefaultColors(void); //--- Конструкторы/деструктор CButtonTriggered (void) : CButton(::ChartID(), "") { this.Init(); } CButtonTriggered (const long chart_id, const string text) : CButton(chart_id, text) { this.Init(); } ~CButtonTriggered (void) {} }; //+------------------------------------------------------------------+ //| Устанавливает цвета по умолчанию | //+------------------------------------------------------------------+ void CButtonTriggered::InitDefaultColors(void) { // standard focused pressed blocked this.SetDefaultColors(COLOR_BACKGROUND, STATE_OFF, C'240,240,240', C'235,235,235', C'234,234,234', clrLightGray); this.SetDefaultColors(COLOR_FOREGROUND, STATE_OFF, C'153,105,129', C'154,106,106', C'152,104,104', clrSilver); this.SetDefaultColors(COLOR_BORDER, STATE_OFF, C'239,239,239', C'234,234,234', C'233,233,233', clrSilver); this.SetDefaultColors(COLOR_BACKGROUND, STATE_ON, clrSnow, C'245,250,250', clrSnow, clrSnow); this.SetDefaultColors(COLOR_FOREGROUND, STATE_ON, C'102,0,58', C'33,0,200', C'102,0,58', clrSilver); this.SetDefaultColors(COLOR_BORDER, STATE_ON, clrBlack, clrBlack, clrBlack, clrSilver); } //+------------------------------------------------------------------+ //| Обработчик нажатия кнопки мышки на объекте | //+------------------------------------------------------------------+ void CButtonTriggered::MousePressHandler(const int id,const long lparam,const double dparam,const string sparam) { //--- Устанавливаем состояние кнопки, обратное уже установленному ENUM_STATE state=(this.State()==STATE_OFF ? STATE_ON : STATE_OFF); this.SetState(state); this.ResetUsedColors(state); //--- Если цвета объекта не соответствуют цветам при щелчке на объекте if(!this.CheckColor(REASON_COLOR_PRESSED)) { //--- устанавливаем нужный цвет и перерисовываем внешний вид this.ColorChange(REASON_COLOR_PRESSED); this.Draw(true); } //--- на щелчок любой кнопкой мышки (sparam = 1: ЛКМ, sparam = 2: ПКМ, sparam = 16: СКМ) //--- отправляем пользовательское событие на график с именем объекта, по которому был щелчок в sparam ::EventChartCustom(this.m_chart_id, (ushort)CHARTEVENT_CLICK, lparam, dparam, this.Name()); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Класс кнопки для вкладки | //+------------------------------------------------------------------+ class CTabButton : public CButtonTriggered { private: //--- Виртуальные обработчики событий virtual void MousePressHandler(const int id, const long lparam, const double dparam, const string sparam); protected: ENUM_ANCHOR_POINT m_placing; // Расположение кнопки относительно поля вкладки public: //--- Переопределяемые виртуальные методы родительского класса virtual int Type(void) const { return(OBJECT_TYPE_TAB_BUTTON); } virtual void Draw(const bool chart_redraw) override; //--- Конструкторы/деструктор CTabButton (void) : CButtonTriggered(::ChartID(), ""), m_placing(ANCHOR_UPPER) { this.Init(); } CTabButton (const long chart_id, const string text) : CButtonTriggered(chart_id, text), m_placing(ANCHOR_UPPER) { this.Init(); } ~CTabButton (void) {} }; //+------------------------------------------------------------------+ //| Рисует внешний вид кнопки | //+------------------------------------------------------------------+ void CTabButton::Draw(const bool chart_redraw) override { int x=0, y=0, w=0, h=0; if(this.m_placing==ANCHOR_UPPER) // в данной версии расположение кнопки только над полем вкладки (ANCHOR_UPPER) { //--- высота рамки на 1 пиксель больше размера кнопки h=this.m_bound.Height(); w=this.m_bound.Width()-1; } //--- Заливаем фон цветом фона и рисуем рамку (нижняя граница рамки на пиксель ниже нижней границы объекта) this.m_canvas.Erase(::ColorToARGB(this.BackColor(), this.m_alpha)); this.m_canvas.Rectangle(x, y, w, h, ::ColorToARGB(this.BorderColor())); //--- Получаем центральную точку и рассчитываем координаты текста CPoint pt=this.m_bound.CenterPoint(); x=pt.x-this.m_bound.left; y=pt.y-this.m_bound.top; //--- Выводим текст с выравниванием по центру и обновляем канвас this.m_canvas.TextOut(x, y, this.Text(), ::ColorToARGB(this.ForeColor(), this.m_alpha), TA_CENTER|TA_VCENTER); CBaseCanvas::UpdateView(chart_redraw); } //+------------------------------------------------------------------+ //| Обработчик нажатия кнопки мышки на объекте | //+------------------------------------------------------------------+ void CTabButton::MousePressHandler(const int id,const long lparam,const double dparam,const string sparam) { //--- Если кнопка в состоянии STATE_ON - уходим if(this.State()) return; //--- Устанавливаем состояние кнопки, обратное уже установленному ENUM_STATE state=(this.State()==STATE_OFF ? STATE_ON : STATE_OFF); this.SetState(state); this.ResetUsedColors(state); //--- Если цвета объекта не соответствуют цветам при щелчке на объекте if(!this.CheckColor(REASON_COLOR_PRESSED)) { //--- устанавливаем нужный цвет и перерисовываем внешний вид this.ColorChange(REASON_COLOR_PRESSED); this.Draw(true); } //--- на щелчок любой кнопкой мышки (sparam = 1: ЛКМ, sparam = 2: ПКМ, sparam = 16: СКМ) //--- отправляем пользовательское событие на график с именем объекта, по которому был щелчок в sparam ::EventChartCustom(this.m_chart_id, (ushort)CHARTEVENT_CLICK, lparam, dparam, this.Name()); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Класс кнопки-переключателя | //+------------------------------------------------------------------+ class CButtonSwitch : public CPanel { private: uchar m_buttons_total; // Количество кнопок в переключателе uint m_spacing; // Дистанция между кнопками uint m_selected_button; // Индекс выбранной (нажатой) кнопки //--- Распределение всех кнопок в ряд по горизонтали int ArrangeButtons(const uint spacing); public: //--- Переопределяемые виртуальные методы родительского класса virtual int Type(void) const { return(OBJECT_TYPE_SBUTTON); } virtual void Draw(const bool chart_redraw) override; virtual bool Create(const string name, const string object_text, const int x, const int y, const int w, const int h) override; virtual bool MoveX(const int x) override; virtual bool MoveY(const int y) override; //--- Программное включение кнопки по индексу void Select(const uint index); //--- Установка дистанции между кнопками void SetSpacing(const uint spacing) { this.m_spacing=spacing; } //--- Возвращает дистанцию между кнопками uint Spacing(void) const { return this.m_spacing; } //--- Добавление новой кнопки в объект bool AddNewButton(const string &butt_texts[], const int width, const uint spacing=0); //--- Возвращает указатель на кнопку по индексу CButtonTriggered *GetButton(const uint index) { return this.GetAttachedObj(index);} //--- Возвращает индекс выбранной (нажатой) кнопки uint SelectedButton(void) const { return this.m_selected_button; } void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Конструкторы/деструктор CButtonSwitch (void) : m_buttons_total(2), m_spacing(0), m_selected_button(0) { this.Init(); } CButtonSwitch (const long chart_id, const uchar buttons, const uchar spacing) : CPanel(chart_id), m_buttons_total(buttons<2 ? 2 : buttons), m_spacing(spacing), m_selected_button(0) { this.Init(); } ~CButtonSwitch (void){} }; //+------------------------------------------------------------------+ //| Создаёт объект кнопку в указанных координатах | //+------------------------------------------------------------------+ bool CButtonSwitch::Create(const string name, const string object_text,const int x,const int y,const int w,const int h) override { //--- Размер создаваемого объекта не может быть меньше 6 пикселей в ширину (2 кнопки по 3 пикселя), и 3 пикселей в высоту return(CPanel::Create(name, object_text, x, y, (w<6 ? 6 : w), (h<3 ? 3 : h))); } //+------------------------------------------------------------------+ //| Добавляет новые кнопки | //+------------------------------------------------------------------+ bool CButtonSwitch::AddNewButton(const string &butt_texts[],const int width,const uint spacing=0) { //--- Если передан пустой массив текстов кнопок - сообщаем об этом и возвращаем false if(butt_texts.Size()==0) { ::PrintFormat("%s: Error. Empty array passed",__FUNCTION__); return false; } //--- Координата X кнопки и количество добавляемых кнопок int x=0, total=(int)butt_texts.Size(); //--- В цикле по количеству добавляемых кнопок for(int i=0; itotal-1) return; //--- В цикле по всем кнопкам объекта for(int i=0;iCHARTEVENT_CUSTOM) { //--- Если кнопка не принадлежит этому объекту - уходим if(::StringFind(sparam, this.Name())==WRONG_VALUE) return; //--- Позиция номера кнопки в строке имени нажатой кнопки в sparam int pos_butt=::StringLen(this.Name())+1; if(pos_butt<0) return; //--- Получаем из строки номер кнопки и преобразуем в число string butt_num_str=::StringSubstr(sparam, pos_butt); if(butt_num_str=="") return; long butt_num=::StringToInteger(butt_num_str); //--- Выбираем кнопку по полученному номеру this.Select((int)butt_num); //--- отправляем пользовательское событие на график программы //--- lparam = номер кнопки //--- dparam - 0 //--- sparam - имя объекта, по которому был щелчок ::EventChartCustom(this.m_chart_id, (ushort)CHARTEVENT_CLICK, butt_num, 0, this.Name()); } } //+------------------------------------------------------------------+ //| Перемещает объект на новую координату x | //+------------------------------------------------------------------+ bool CButtonSwitch::MoveX(const int x) { //--- Смещаем родительский объект на указанную координату if(!CBaseCanvas::MoveX(x)) return false; //--- В цикле по всем привязанным кнопкам смещаем каждую, записывая результат в переменную res bool res=true; int total=this.m_list_obj.Total(); for(int i=0; iCHARTEVENT_CUSTOM) { //--- Если имя объекта в sparam соответствует вкладке (начинается с "Tab") if(::StringFind(sparam, "Tab")==0) { //--- расщепляем строку по разделителю "_" string array[]={}; if(::StringSplit(sparam, ::StringGetCharacter("_", 0), array)==2) { //--- Здесь имя вкладки string tab_name=array[0]; //--- Из имени извлекаем номер вкладки с позиции 3 //string num=::StringSubstr(tab_name, 3); //--- Если событие пришло от щелчка по кнопке вкладки (после разделителя в строке запись "Button") if(array[1]=="Button") { //--- Получаем указатель на вкладку по её имени CTab *tab=this.GetTabByName(tab_name); //--- Если указатель получен if(tab!=NULL) { //--- выбираем вкладку tab.Select(); //--- В цикле по всем привязанным объектам int total=this.m_list_obj.Total(); for(int i=0;i