
DoEasy. Controles (Parte 25): Objeto WinForms Tooltip
Conteúdo
- Ideia
- Modificando as classes da biblioteca
- Classe do objeto WinForms ToolTip
- Teste
- O que virá a seguir?
Ideia
Quando você passa o mouse sobre um controle, aparece uma dica de ferramenta com uma descrição depois de um tempo. Naturalmente, nem todo elemento tem uma dica de ferramenta, mas todo objeto gráfico pode ter uma. Vamos gerar esse comportamento para os elementos gráficos da biblioteca.
Nem todo elemento gráfico na biblioteca "sabe" da existência de outros elementos gráficos, mas todos esses objetos podem anexar elementos gerenciados a si mesmos. Os objetos anexados a um elemento gráfico podem incluir objetos sobre os quais o elemento pai nada conhece. Como o gerenciamento de elementos anexados ocorre na classe coleção de elementos gráficos, não há problemas com a visibilidade de vários tipos de controles, uma vez que a classe de coleção sabe tudo sobre eles.
Além disso, se usarmos o MS Visual Studio como exemplo, o objeto ToolTip criado ficará visível no objeto-forma e poderá ser atribuído a objetos vinculados à forma em questão. Faremos com que os objetos que verão a classe do objeto dica de ferramenta consigam criá-lo e que os outros objetos possam anexar a si mesmos o objeto criado anteriormente. Para isso, eles não precisam "conhece-lo", pois qualquer objeto derivado da classe base da Biblioteca Padrão pode ser adicionado à lista ArrayObj .
Em outras palavras, em objetos contêiner (que criam e anexam outros objetos gráficos a si mesmos), podemos criar controles tooltip e indicar a qual elemento atribuir essa dica de ferramenta. Neste elemento será registrado um ponteiro para o objeto ToolTip, e no objeto ToolTip designado será especificado o objeto para o qual esta dica de ferramenta foi atribuída.
Normalmente, a dica de ferramenta aparece depois que o cursor paira sobre o controle por algum tempo Se várias dicas forem atribuídas a um objeto e a primeira dica já tiver aparecido, ao mover o cursor sobre esses objetos, as dicas de ferramenta restantes aparecerão quase sem demora. Trataremos desse comportamento nos tópicos dos artigos posteriores. Hoje vamos apenas criar o controle ToolTip e atribui-lo a elementos gráficos.
Além disso, hoje implementaremos novas primitivas gráficas, como ícones padrão, setas direita-esquerda, cima-baixo... Basicamente, começaremos lentamente a adicionar desenhos predefinidos à biblioteca, que serão usados "tal como estão". Esses desenhos, neste caso ícones, podem ser usados nos controles ToolTip que criaremos hoje e em outros elementos que já criamos anteriormente. No futuro, com o desenvolvimento da biblioteca, adicionaremos a capacidade de usar novas primitivas gráficas em todos os elementos gráficos da biblioteca.
Modificando as classes da biblioteca
No arquivo \MQL5\Include\DoEasy\Defines.mqh, adicionamos um novo tipo de elemento gráfico à lista de tipos de elementos gráficos:
//+------------------------------------------------------------------+ //| The list of graphical element types | //+------------------------------------------------------------------+ enum ENUM_GRAPH_ELEMENT_TYPE { GRAPH_ELEMENT_TYPE_STANDARD, // Standard graphical object GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED, // Extended standard graphical object GRAPH_ELEMENT_TYPE_SHADOW_OBJ, // Shadow object GRAPH_ELEMENT_TYPE_ELEMENT, // Element GRAPH_ELEMENT_TYPE_FORM, // Form GRAPH_ELEMENT_TYPE_WINDOW, // Window //--- WinForms GRAPH_ELEMENT_TYPE_WF_UNDERLAY, // Panel object underlay GRAPH_ELEMENT_TYPE_WF_BASE, // Windows Forms Base //--- 'Container' object types are to be set below GRAPH_ELEMENT_TYPE_WF_CONTAINER, // Windows Forms container base object GRAPH_ELEMENT_TYPE_WF_PANEL, // Windows Forms Panel GRAPH_ELEMENT_TYPE_WF_GROUPBOX, // Windows Forms GroupBox GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL, // Windows Forms TabControl GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER, // Windows Forms SplitContainer //--- 'Standard control' object types are to be set below GRAPH_ELEMENT_TYPE_WF_COMMON_BASE, // Windows Forms base standard control GRAPH_ELEMENT_TYPE_WF_LABEL, // Windows Forms Label GRAPH_ELEMENT_TYPE_WF_BUTTON, // Windows Forms Button GRAPH_ELEMENT_TYPE_WF_CHECKBOX, // Windows Forms CheckBox GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON, // Windows Forms RadioButton GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX, // Base list object of Windows Forms elements GRAPH_ELEMENT_TYPE_WF_LIST_BOX, // Windows Forms ListBox GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX, // Windows Forms CheckedListBox GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX, // Windows Forms ButtonListBox GRAPH_ELEMENT_TYPE_WF_TOOLTIP, // Windows Forms ToolTip //--- Auxiliary elements of WinForms objects GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM, // Windows Forms ListBoxItem GRAPH_ELEMENT_TYPE_WF_TAB_HEADER, // Windows Forms TabHeader GRAPH_ELEMENT_TYPE_WF_TAB_FIELD, // Windows Forms TabField GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL, // Windows Forms SplitContainerPanel GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON, // Windows Forms ArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP, // Windows Forms UpArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN, // Windows Forms DownArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT, // Windows Forms LeftArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT, // Windows Forms RightArrowButton GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX, // Windows Forms UpDownArrowButtonsBox GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX, // Windows Forms LeftRightArrowButtonsBox GRAPH_ELEMENT_TYPE_WF_SPLITTER, // Windows Forms Splitter GRAPH_ELEMENT_TYPE_WF_HINT_BASE, // Windows Forms HintBase GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT, // Windows Forms HintMoveLeft GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT, // Windows Forms HintMoveRight GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP, // Windows Forms HintMoveUp GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN, // Windows Forms HintMoveDown }; //+------------------------------------------------------------------+
Para desenhar os ícones "Info", "Warning", "Error", etc, vamos criar uma enumeração com os tipos correspondentes:
//+------------------------------------------------------------------+ //| Separator location in Split Container | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_SPLITTER_ORIENTATION { CANV_ELEMENT_SPLITTER_ORIENTATION_VERTICAL, // Vertical CANV_ELEMENT_SPLITTER_ORIENTATION_HORISONTAL, // Horizontal }; //+------------------------------------------------------------------+ //| The list of predefined icons | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_TOOLTIP_ICON { CANV_ELEMENT_TOOLTIP_ICON_NONE, // None CANV_ELEMENT_TOOLTIP_ICON_INFO, // Info CANV_ELEMENT_TOOLTIP_ICON_WARNING, // Warning CANV_ELEMENT_TOOLTIP_ICON_ERROR, // Error CANV_ELEMENT_TOOLTIP_ICON_USER, // User }; //+------------------------------------------------------------------+ //| Integer properties of the graphical element on the canvas | //+------------------------------------------------------------------+
Aqui inserimos os nomes dos novos ícones que iremos criar na biblioteca. E então podemos simplesmente selecionar na lista o tipo de ícone que queremos desenhar no controle. Em seguida, serão criados métodos para desenhar cada tipo específico de ícone a partir dessa lista.
O novo controle exigirá novas propriedades.
Adicionamos novas propriedades à lista de propriedades inteiras do elemento gráfico na tela e aumentamos seu número total de 122 para 129:
//+------------------------------------------------------------------+ //| Integer properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_INTEGER { CANV_ELEMENT_PROP_ID = 0, // Element ID CANV_ELEMENT_PROP_TYPE, // Graphical element type //---... //---... CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,// Flag for collapsed panel 2 CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE, // Panel 2 minimum size CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY, // Tooltip display delay CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY, // Tooltip display duration CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY, // One element new tooltip display delay CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS, // Display a tooltip in inactive window CANV_ELEMENT_PROP_TOOLTIP_ICON, // Icon displayed in tooltip CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON, // Tooltip in the form of a "cloud" CANV_ELEMENT_PROP_TOOLTIP_USE_FADING, // Fade when showing/hiding a tooltip }; #define CANV_ELEMENT_PROP_INTEGER_TOTAL (129) // Total number of integer properties #define CANV_ELEMENT_PROP_INTEGER_SKIP (0) // Number of integer properties not used in sorting //+------------------------------------------------------------------+
Vamos adicionar duas novas propriedades à lista de propriedades de string e aumentar seu número total para 6:
//+------------------------------------------------------------------+ //| String properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_STRING { CANV_ELEMENT_PROP_NAME_OBJ = (CANV_ELEMENT_PROP_INTEGER_TOTAL+CANV_ELEMENT_PROP_DOUBLE_TOTAL), // Graphical element object name CANV_ELEMENT_PROP_NAME_RES, // Graphical resource name CANV_ELEMENT_PROP_TEXT, // Graphical element text CANV_ELEMENT_PROP_DESCRIPTION, // Graphical element description CANV_ELEMENT_PROP_TOOLTIP_TITLE, // Element tooltip title CANV_ELEMENT_PROP_TOOLTIP_TEXT, // Element tooltip text }; #define CANV_ELEMENT_PROP_STRING_TOTAL (6) // Total number of string properties //+------------------------------------------------------------------+
E adicionamos estas novas propriedades à lista de possíveis critérios para ordenar elementos gráficos na tela:
//+------------------------------------------------------------------+ //| Possible sorting criteria of graphical elements on the canvas | //+------------------------------------------------------------------+ #define FIRST_CANV_ELEMENT_DBL_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP) #define FIRST_CANV_ELEMENT_STR_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP) enum ENUM_SORT_CANV_ELEMENT_MODE { //--- Sort by integer properties SORT_BY_CANV_ELEMENT_ID = 0, // Sort by element ID SORT_BY_CANV_ELEMENT_TYPE, // Sort by graphical element type //---... //---... SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL2_COLLAPSED,// Sort by flag for collapsed panel 2 SORT_BY_CANV_ELEMENT_SPLIT_CONTAINER_PANEL2_MIN_SIZE, // Sort by panel 2 minimum size SORT_BY_CANV_ELEMENT_TOOLTIP_INITIAL_DELAY, // Sort by tooltip display delay SORT_BY_CANV_ELEMENT_TOOLTIP_AUTO_POP_DELAY, // Sort by tooltip display duration SORT_BY_CANV_ELEMENT_TOOLTIP_RESHOW_DELAY, // Sort by one element new tooltip display delay SORT_BY_CANV_ELEMENT_TOOLTIP_SHOW_ALWAYS, // Sort by a tooltip in inactive window SORT_BY_CANV_ELEMENT_TOOLTIP_ICON, // Sort by icon displayed in a tooltip SORT_BY_CANV_ELEMENT_TOOLTIP_IS_BALLOON, // Sort by a cloud tooltip flag SORT_BY_CANV_ELEMENT_TOOLTIP_USE_FADING, // Sort by the flag of fading when showing/hiding a tooltip //--- Sort by real properties //--- Sort by string properties SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by an element object name SORT_BY_CANV_ELEMENT_NAME_RES, // Sort by the graphical resource name SORT_BY_CANV_ELEMENT_TEXT, // Sort by graphical element text SORT_BY_CANV_ELEMENT_DESCRIPTION, // Sort by graphical element description SORT_BY_CANV_ELEMENT_TOOLTIP_HEADER, // Sort by ToolTip header for an element SORT_BY_CANV_ELEMENT_TOOLTIP_TEXT, // Sort by ToolTip text for an element }; //+------------------------------------------------------------------+
Agora poderemos ordenar, selecionar e filtrar todos os elementos gráficos por novas propriedades.
No arquivo \MQL5\Include\DoEasy\Data.mqh, adicionamos os índices das novas mensagens:
//--- CForm MSG_FORM_OBJECT_TEXT_NO_SHADOW_OBJ_FIRST_CREATE_IT,// No shadow object. Create it using the CreateShadowObj() method MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ, // Failed to create new shadow object MSG_FORM_OBJECT_ERR_FAILED_CREATE_PC_OBJ, // Failed to create new pixel copier object MSG_FORM_OBJECT_PC_OBJ_ALREADY_IN_LIST, // Pixel copier object with ID already present in the list MSG_FORM_OBJECT_PC_OBJ_NOT_EXIST_LIST, // No pixel copier object with ID in the list MSG_FORM_OBJECT_ERR_NOT_INTENDED, // The method is not meant for creating such an object: MSG_FORM_TOOLTIP_OBJ_ALREADY_EXISTS, // ToolTip object already exists //--- CFrame
...
MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP, // HintMoveLeft control MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN, // HintMoveLeft control MSG_GRAPH_ELEMENT_TYPE_WF_TOOLTIP, // ToolTip control MSG_GRAPH_OBJ_BELONG_PROGRAM, // Graphical object belongs to a program MSG_GRAPH_OBJ_BELONG_NO_PROGRAM, // Graphical object does not belong to a program
...
MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED, // Flag for collapsed panel 1 MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE, // Panel 2 minimum size MSG_CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY, // Tooltip display delay MSG_CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY, // Tooltip display duration MSG_CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY, // One element new tooltip display delay MSG_CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS, // Display a tooltip in inactive window MSG_CANV_ELEMENT_PROP_TOOLTIP_ICON, // Icon displayed in a tooltip MSG_CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON, // Tooltip in the form of a "cloud" MSG_CANV_ELEMENT_PROP_TOOLTIP_USE_FADING, // Fade when showing/hiding a tooltip //--- Real properties of graphical elements //--- String properties of graphical elements MSG_CANV_ELEMENT_PROP_NAME_OBJ, // Graphical element object name MSG_CANV_ELEMENT_PROP_NAME_RES, // Graphical resource name MSG_CANV_ELEMENT_PROP_TEXT, // Graphical element text MSG_CANV_ELEMENT_PROP_DESCRIPTION, // Graphical element description MSG_CANV_ELEMENT_PROP_TOOLTIP_TITLE, // Element tooltip title MSG_CANV_ELEMENT_PROP_TOOLTIP_TEXT, // Element tooltip text }; //+------------------------------------------------------------------+
e mensagens de texto correspondentes aos índices recém-adicionados:
//--- CForm {"Отсутствует объект тени. Необходимо сначала его создать при помощи метода CreateShadowObj()","There is no shadow object. You must first create it using the CreateShadowObj () method"}, {"Не удалось создать новый объект для тени","Failed to create new object for shadow"}, {"Не удалось создать новый объект-копировщик пикселей","Failed to create new pixel copier object"}, {"В списке уже есть объект-копировщик пикселей с идентификатором ","There is already a pixel copier object in the list with ID "}, {"В списке нет объекта-копировщика пикселей с идентификатором ","No pixel copier object with ID "}, {"Метод не предназначен для создания такого объекта: ","The method is not intended to create such an object: "}, {"Объект ToolTip уже существует","ToolTip object already exists"}, //--- CFrame
...
{"Элемент управления \"HintMoveUp\"","Control element \"HintMoveUp\""}, {"Элемент управления \"HintMoveDown\"","Control element \"HintMoveDown\""}, {"Элемент управления \"ToolTip\"","Control element \"ToolTip\""}, {"Графический объект принадлежит программе","The graphic object belongs to the program"}, {"Графический объект не принадлежит программе","The graphic object does not belong to the program"},
...
{"Флаг свёрнутости панели 2","Flag to indicate that panel 2 is collapsed"}, {"Минимальный размер панели 2","Min size of Panel 2"}, {"Задержка отображения подсказки","Tooltip initial delay"}, {"Длительность отображения подсказки","Tooltip autopoop delay"}, {"Задержка отображения новой подсказки одного элемента","Tooltip reshow delay"}, {"Отображать подсказку в неактивном окне","Tooltip show always"}, {"Значок, отображаемый в подсказке","Tooltip icon"}, {"Подсказка в форме \"облачка\"","Tooltip as \"Balloon\""}, {"Угасание при отображении и скрытии подсказки","Tooltip uses fading"}, //--- String properties of graphical elements {"Имя объекта-графического элемента","The name of the graphic element object"}, {"Имя графического ресурса","Image resource name"}, {"Текст графического элемента","Text of the graphic element"}, {"Описание графического элемента","Description of the graphic element"}, {"Заголовок подсказки элемента","Element tooltip header"}, {"Текст подсказки элемента","Element tooltip title"}, }; //+---------------------------------------------------------------------+
Para obtermos uma descrição de um elemento gráfico, o arquivo \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh do objeto gráfico base da biblioteca contém o método TypeElementDescription(). Vamos escrever nele o retorno da descrição do novo controle:
//+------------------------------------------------------------------+ //| Return the description of the graphical element type | //+------------------------------------------------------------------+ string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type) { return ( type==GRAPH_ELEMENT_TYPE_STANDARD ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD) : type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) : type==GRAPH_ELEMENT_TYPE_ELEMENT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT) : type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ) : type==GRAPH_ELEMENT_TYPE_FORM ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM) : type==GRAPH_ELEMENT_TYPE_WINDOW ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW) : //--- WinForms type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY) : type==GRAPH_ELEMENT_TYPE_WF_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE) : //--- Containers type==GRAPH_ELEMENT_TYPE_WF_CONTAINER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER) : type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX) : type==GRAPH_ELEMENT_TYPE_WF_PANEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL) : type==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL) : type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER) : //--- Standard controls type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE) : type==GRAPH_ELEMENT_TYPE_WF_LABEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL) : type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX) : type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON) : type==GRAPH_ELEMENT_TYPE_WF_BUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON) : type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM) : type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_TOOLTIP ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TOOLTIP) : //--- Auxiliary control objects type==GRAPH_ELEMENT_TYPE_WF_TAB_HEADER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER) : type==GRAPH_ELEMENT_TYPE_WF_TAB_FIELD ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_FIELD) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX) : type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL) : type==GRAPH_ELEMENT_TYPE_WF_SPLITTER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLITTER) : type==GRAPH_ELEMENT_TYPE_WF_HINT_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_BASE) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN) : "Unknown" ); } //+------------------------------------------------------------------+
Ao método é passado o tipo do objeto cuja descrição se deseja obter e, dependendo do tipo passado e do índice da mensagem que adicionamos acima ao arquivo Data.mqh, uma descrição é exibida.
Anteriormente, especificávamos manualmente o objeto base de um elemento gráfico quando criávamos um elemento gráfico anexado.
Agora (no último artigo) nos livramos da necessidade de fazer isso e removemos os métodos de escrita de ponteiros para os objetos principal e base no objeto. Mas, a prática nós diz que ainda precisamos de tais métodos. Para escrever em um novo objeto de dica de ferramenta o elemento para o qual ela foi criada, precisamos de um método de escrita de ponteiro para o objeto base.
Na seção pública do arquivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, escrevemos novamente o seguinte método:
public: //--- (1) Set and (2) return the X coordinate shift relative to the base object void SetCoordXRelative(const int value) { this.m_shift_coord_x=value; } int CoordXRelative(void) const { return this.m_shift_coord_x; } //--- (1) Set and (2) return the Y coordinate shift relative to the base object void SetCoordYRelative(const int value) { this.m_shift_coord_y=value; } int CoordYRelative(void) const { return this.m_shift_coord_y; } //--- Set the pointer to the parent element within related objects of the current group void SetBase(CGCnvElement *base) { this.m_element_base=base; } //--- Event handler virtual void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
O método recebe um ponteiro para o objeto base, que é atribuído à variável m_element_base. Assim, para indicar para qual objeto foi criada uma dica de ferramenta, escreveremos um ponteiro para o elemento sobre qual está o mouse.
Vamos tornar os métodos de definição da opacidade do elemento e de definição do sinalizador de exibição do elemento virtuais, pois precisaremos redefini-los posteriormente nas classes herdadas. E adicionamos métodos para definir e retornar o texto na dica de ferramenta:
//--- Set the shift of the (1) left, (2) top, (3) right, (4) bottom edge of the active area relative to the element, //--- (5) all shifts of the active area edges relative to the element, (6) opacity void SetActiveAreaLeftShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,fabs(value)); } void SetActiveAreaRightShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,fabs(value)); } void SetActiveAreaTopShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,fabs(value)); } void SetActiveAreaBottomShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,fabs(value)); } void SetActiveAreaShift(const int left_shift,const int bottom_shift,const int right_shift,const int top_shift); virtual void SetOpacity(const uchar value,const bool redraw=false); //--- (1) Set and (2) return the Tooltip text virtual void SetTooltipText(const string text) { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,text); } virtual string TooltipText(void) { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT); } //--- (1) Set and (2) return the flag for displaying a non-hidden control virtual void SetDisplayed(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,flag); } bool Displayed(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_DISPLAYED); }
No corpo do construtor, temos uma lista de métodos para desenhar diversas primitivas gráficas. No final da lista, declararemos novos métodos para desenhar imagens "padrão" predefinidas:
//--- Return coordinate offsets relative to the rectangle anchor point by size void GetShiftXYbySize(const int width, // Rectangle size by width const int height, //Rectangle size by height const ENUM_FRAME_ANCHOR anchor,// Rectangle anchor point, relative to which the offsets are calculated int &shift_x, // X coordinate of the rectangle upper left corner int &shift_y); // Y coordinate of the rectangle upper left corner //+------------------------------------------------------------------+ //| Methods for drawing predefined standard images | //+------------------------------------------------------------------+ //--- Draw the Info icon void DrawIconInfo(const int coord_x,const int coord_y,const uchar opacity); //--- Draw the Warning icon void DrawIconWarning(const int coord_x,const int coord_y,const uchar opacity); //--- Draw the Error icon void DrawIconError(const int coord_x,const int coord_y,const uchar opacity); //--- Draw the left arrow void DrawArrowLeft(const int coord_x,const int coord_y,const color clr,const uchar opacity); //--- Draw the right arrow void DrawArrowRight(const int coord_x,const int coord_y,const color clr,const uchar opacity); //--- Draw the up arrow void DrawArrowUp(const int coord_x,const int coord_y,const color clr,const uchar opacity); //--- Draw the down arrow void DrawArrowDown(const int coord_x,const int coord_y,const color clr,const uchar opacity); }; //+------------------------------------------------------------------+
Em ambos os construtores de classe, definiremos os valores padrão para as novas propriedades do elemento gráfico:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main_obj,CGCnvElement *base_obj, const int element_id, const int element_num, const long chart_id, const int wnd_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable=true, const bool activity=true, const bool redraw=false) : m_shadow(false) { this.SetTypeElement(element_type); this.m_type=OBJECT_DE_TYPE_GELEMENT; this.m_element_main=main_obj; this.m_element_base=base_obj; this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND); this.m_name=this.CreateNameGraphElement(element_type); this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id); this.m_subwindow=wnd_num; this.SetFont(DEF_FONT,DEF_FONT_SIZE); this.m_text_anchor=0; this.m_text_x=0; this.m_text_y=0; this.SetBackgroundColor(colour,true); this.SetOpacity(opacity); this.m_shift_coord_x=0; this.m_shift_coord_y=0; if(::ArrayResize(this.m_array_colors_bg,1)==1) this.m_array_colors_bg[0]=this.BackgroundColor(); if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1) this.m_array_colors_bg_dwn[0]=this.BackgroundColor(); if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1) this.m_array_colors_bg_ovr[0]=this.BackgroundColor(); if(this.Create(chart_id,wnd_num,x,y,w,h,redraw)) { this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID()); // Chart ID //---... //---... this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,false); // Flag for collapsed panel 1 this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,25); // Panel 2 minimum size this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,500); // Tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,5000); // Tooltip display duration this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,100); // One element new tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,false); // Display a tooltip in inactive window this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,CANV_ELEMENT_TOOLTIP_ICON_NONE); // Icon displayed in a tooltip this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,false); // Tooltip in the form of a "cloud" this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,true); // Fade when showing/hiding a tooltip this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,""); // Tooltip title for the element this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,""); // Tooltip text for the element this.SetVisibleFlag(false,false); } else { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj()); } } //+------------------------------------------------------------------+ //| Protected constructor | //+------------------------------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int wnd_num, const string descript, const int x, const int y, const int w, const int h) : m_shadow(false) { this.m_type=OBJECT_DE_TYPE_GELEMENT; this.m_element_main=main_obj; this.m_element_base=base_obj; this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND); this.m_name=this.CreateNameGraphElement(element_type); this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id); this.m_subwindow=wnd_num; this.m_type_element=element_type; this.SetFont(DEF_FONT,DEF_FONT_SIZE); this.m_text_anchor=0; this.m_text_x=0; this.m_text_y=0; this.SetBackgroundColor(CLR_CANV_NULL,true); this.SetOpacity(0); this.m_shift_coord_x=0; this.m_shift_coord_y=0; if(::ArrayResize(this.m_array_colors_bg,1)==1) this.m_array_colors_bg[0]=this.BackgroundColor(); if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1) this.m_array_colors_bg_dwn[0]=this.BackgroundColor(); if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1) this.m_array_colors_bg_ovr[0]=this.BackgroundColor(); if(this.Create(chart_id,wnd_num,x,y,w,h,false)) { this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID()); // Chart ID //---... //---... this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE,25); // Panel 1 minimum size this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,false); // Flag for collapsed panel 1 this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,25); // Panel 2 minimum size this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,500); // Tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,5000); // Tooltip display duration this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,100); // One element new tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,false); // Display a tooltip in inactive window this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,CANV_ELEMENT_TOOLTIP_ICON_NONE); // Icon displayed in a tooltip this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,false); // Tooltip in the form of a "cloud" this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,true); // Fade when showing/hiding a tooltip this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,""); // Tooltip title for the element this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,""); // Tooltip text for the element this.SetVisibleFlag(false,false); } else { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj()); } } //+------------------------------------------------------------------+
Fora do corpo da classe, escreveremos métodos para desenhar imagens predefinidas.
Método que desenha o ícone Info:
//+------------------------------------------------------------------+ //| Draw the Info icon | //+------------------------------------------------------------------+ void CGCnvElement::DrawIconInfo(const int coord_x,const int coord_y,const uchar opacity) { int x=coord_x+8; int y=coord_y+8; this.DrawCircleFill(x,y,7,C'0x00,0x77,0xD7',opacity); this.DrawCircleWu(x,y,7.5,C'0x00,0x3D,0x8C',opacity); this.DrawRectangle(x,y-5,x+1,y-4, C'0xFF,0xFF,0xFF',opacity); this.DrawRectangle(x,y-2,x+1,y+4,C'0xFF,0xFF,0xFF',opacity); } //+------------------------------------------------------------------+
O método recebe as coordenadas do canto superior esquerdo que delimita a imagem retangular. Em seguida, são calculadas as coordenadas do ponto central do círculo e desenhado um círculo preenchido. Acima dele, é desenhada uma circunferência suavizada com o método Wu. Depois de desenhadas as circunferências, é desenhada uma letra "i", composta por dois retângulos, sendo que o primeiro desenha um ponto com 2x2 pixels, e o segundo, uma linha vertical com 2x6 pixels. Infelizmente, não é possível desenhar linhas suavizadas com a espessura desejada (LineThick) devido a um algoritmo estranho de suavização aplicado ao desenho de tais linhas, resultando em uma imagem pouco atraente. Portanto, para desenhar linhas com espessura de dois pixels, simplesmente desenhamos um retângulo com dois pixels de largura e a altura necessária. Todas as cores do método já estão pré-definidas para que a imagem pareça padrão como no MS Visual Studio.
Método que desenha o ícone Warning:
//+------------------------------------------------------------------+ //| Draw the Warning icon | //+------------------------------------------------------------------+ void CGCnvElement::DrawIconWarning(const int coord_x,const int coord_y,const uchar opacity) { int x=coord_x+8; int y=coord_y+1; this.DrawTriangleFill(x,y,x+8,y+14,x-8,y+14,C'0xFC,0xE1,0x00',opacity); this.DrawTriangleWu(x,y,x+8,y+14,x-7,y+14,C'0xFF,0xB9,0x00',opacity); this.DrawRectangle(x,y+5,x+1,y+9, C'0x00,0x00,0x00',opacity); this.DrawRectangle(x,y+11,x+1,y+12,C'0x00,0x00,0x00',opacity); } //+------------------------------------------------------------------+
O método recebe as coordenadas do canto superior esquerdo que delimita a imagem retangular. Em seguida, as coordenadas do ponto superior do triângulo são calculadas, um triângulo preenchido é desenhado e um triângulo com suavização pelo método Wu é desenhado no topo. O segundo triângulo tem a coordenada X de seu vértice deslocada um pixel para a direita em comparação com o primeiro triângulo preenchido. Isso é feito para deixar o vértice visualmente mais grosso, já que o ícone dentro dos triângulos tem que ter dois pixels de largura e não pode ser posicionado exatamente no centro do triângulo. Assim, a ponta do triângulo é "espalhada" por dois pixels, o que centraliza visualmente o símbolo dentro da imagem. Depois de desenhadas as circunferências, é desenhado o signo "!", composto por dois retângulos, sendo que o primeiro desenha um ponto com 2x2 pixels, e o segundo, uma linha vertical com 2x6 pixels.
Método que desenha o ícone Error:
//+------------------------------------------------------------------+ //| Draw the Error icon | //+------------------------------------------------------------------+ void CGCnvElement::DrawIconError(const int coord_x,const int coord_y,const uchar opacity) { int x=coord_x+8; int y=coord_y+8; this.DrawCircleFill(x,y,7,C'0xF0,0x39,0x16',opacity); this.DrawCircleWu(x,y,7.5,C'0xA5,0x25,0x12',opacity); this.DrawLineWu(x-3,y-3,x+3,y+3,C'0xFF,0xFF,0xFF',opacity); this.DrawLineWu(x+3,y-3,x-3,y+3,C'0xFF,0xFF,0xFF',opacity); } //+------------------------------------------------------------------+
O método desenha duas círculos, um preenchido com uma cor e outro desenhado com suavização, e no centro há duas linhas suavizadas formando o símbolo "X". As cores, assim como em outros ícones, são predefinidas para corresponder ao MS Visual Studio.
Métodos que desenham setas para a esquerda, para a direita, para cima e para baixo:
//+------------------------------------------------------------------+ //| Draw the left arrow | //+------------------------------------------------------------------+ void CGCnvElement::DrawArrowLeft(const int coord_x,const int coord_y,const color clr,const uchar opacity) { int x=coord_x; int y=coord_y+5; this.DrawTriangleFill(x,y,x+3,y-3,x+3,y+3,clr,opacity); this.DrawTriangleWu(x,y,x+3,y-3,x+3,y+3,clr,opacity); } //+------------------------------------------------------------------+ //| Draw the right arrow | //+------------------------------------------------------------------+ void CGCnvElement::DrawArrowRight(const int coord_x,const int coord_y,const color clr,const uchar opacity) { int x=coord_x; int y=coord_y+5; this.DrawTriangleFill(x+3,y,x,y+3,x,y-3,clr,opacity); this.DrawTriangleWu(x+3,y,x,y+3,x,y-3,clr,opacity); } //+------------------------------------------------------------------+ //| Draw the up arrow | //+------------------------------------------------------------------+ void CGCnvElement::DrawArrowUp(const int coord_x,const int coord_y,const color clr,const uchar opacity) { int x=coord_x+5; int y=coord_y; this.DrawTriangleFill(x,y,x+3,y+3,x-3,y+3,clr,opacity); this.DrawTriangleWu(x,y,x+3,y+3,x-3,y+3,clr,opacity); } //+------------------------------------------------------------------+ //| Draw the down arrow | //+------------------------------------------------------------------+ void CGCnvElement::DrawArrowDown(const int coord_x,const int coord_y,const color clr,const uchar opacity) { int x=coord_x+5; int y=coord_y+3; this.DrawTriangleFill(x,y,x+3,y-3,x-3,y-3,clr,opacity); this.DrawTriangleWu(x,y,x+3,y-3,x-3,y-3,clr,opacity); } //+------------------------------------------------------------------+
Todos os métodos recebem as coordenadas do canto superior esquerdo do retângulo, a cor e a opacidade das setas desenhadas. O método desenha duas círculos, um preenchido com uma cor e outro desenhado com suavização, e no centro há duas linhas suavizadas formando o símbolo "X". Para cada vértice subsequente do triângulo desenhado, as coordenadas são calculadas com um deslocamento em relação ao primeiro vértice. A cor e a opacidade são especificadas para que a aparência dos triângulos desenhados possa ser alterada de acordo com o estado do elemento gráfico no qual a seta é desenhada. Por exemplo, para um elemento inativo, a seta deve ser cinza. Neste caso, há um pouco mais de possibilidades do que para os ícones.
O objeto sombra sempre existe em conjunto com outro objeto. A opacidade do fundo no qual a sombra é desenhada deve ser sempre zero, porque o objeto deve ser sempre transparente. Ao mesmo tempo, a sombra desenhada neste fundo também deve ter algum grau de transparência, pois a sombra ficaria estranha se cobrisse completamente os objetos sobre os quais é projetada. Portanto, para o objeto sombra, introduzimos o conceito de opacidade de sombra desenhada. E é esse valor que deve mudar quando o método SetOpacity() for chamado, deixando o fundo sempre transparente. Mas para o fundo, o mesmo método define seu valor de opacidade. Então, vamos adicionar um novo método (SetOpacityDraw, a opacidade da sombra desenhada) e combinar esses dois métodos. Ao serem chamados, a opacidade do fundo sempre será definida como zero e, para a cor da sombra desenhada, será definido o valor especificado no método.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh, renomeamos os métodos SetOpacity() e Opacity() e declaramos o método virtual SetOpacity():
//--- Draw an object shadow void Draw(const int shift_x,const int shift_y,const uchar blur_value,const bool redraw); //--- Set the element opacity virtual void SetOpacity(const uchar value,const bool redraw=false); //+------------------------------------------------------------------+ //| Methods of simplified access to object properties | //+------------------------------------------------------------------+ //--- (1) Set and (2) return the shadow color void SetColor(const color colour) { this.m_color=colour; } color Color(void) const { return this.m_color; } //--- (1) Set and (2) return the drawn shadow opacity void SetOpacityDraw(const uchar opacity) { this.m_opacity=opacity; } uchar OpacityDraw(void) const { return this.m_opacity; } //--- (1) Set and (2) return the shadow blur void SetBlur(const uchar blur) { this.m_blur=blur; } uchar Blur(void) const { return this.m_blur; } }; //+------------------------------------------------------------------+
No construtor da classe, definimos os valores padrão para a opacidade da sombra desenhada e seu desfoque:
//+------------------------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CShadowObj::CShadowObj(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_SHADOW_OBJ,main_obj,base_obj,chart_id,subwindow,name,x,y,w,h) { this.m_type=OBJECT_DE_TYPE_GSHADOW; CGCnvElement::SetBackgroundColor(clrNONE,true); CGCnvElement::SetOpacity(0); CGCnvElement::SetActive(false); this.SetOpacityDraw(CLR_DEF_SHADOW_OPACITY); this.SetBlur(DEF_SHADOW_BLUR); color gray=CGCnvElement::ChangeColorSaturation(this.ChartBackgroundColor(),-100); this.m_color=CGCnvElement::ChangeColorLightness(gray,-50); this.m_shadow=false; this.SetVisibleFlag(false,false); CGCnvElement::Erase(); } //+------------------------------------------------------------------+
No método que desenha a forma da sombra do objeto, usaremos o valor de opacidade da sombra para especificar a opacidade:
//+------------------------------------------------------------------+ //| Draw the object shadow form | //+------------------------------------------------------------------+ void CShadowObj::DrawShadowFigureRect(const int w,const int h) { CGCnvElement::DrawRectangleFill(OUTER_AREA_SIZE,OUTER_AREA_SIZE,OUTER_AREA_SIZE+w-1,OUTER_AREA_SIZE+h-1,this.m_color,this.OpacityDraw()); CGCnvElement::Update(); } //+------------------------------------------------------------------+
Escrevemos um novo método virtual que define a opacidade do elemento:
//+------------------------------------------------------------------+ //| Set the element opacity | //+------------------------------------------------------------------+ void CShadowObj::SetOpacity(const uchar value,const bool redraw=false) { CGCnvElement::SetOpacity(0,false); this.SetOpacityDraw(value>(uchar)CLR_DEF_SHADOW_OPACITY ? (uchar)CLR_DEF_SHADOW_OPACITY : value); this.m_canvas.Update(redraw); } //+------------------------------------------------------------------+
Quando esse método é chamado, ele define transparência total para o plano de fundo no qual a sombra é desenhada e, em seguida, para a sombra desenhada, define o valor de opacidade passado para o método. Nesse caso, se o valor exceder o valor de opacidade de sombra padrão definido para a biblioteca, ele será usado. Isso é feito para evitar o aparecimento de sombras opacas cobrindo completamente os objetos subjacentes.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh, vamos mudar a lógica do método que redesenha o objeto. Anteriormente, a sombra do objeto sempre era desenhada quando ele estava presente. Nesse caso, se o sinalizador de redesenho era redefinido, a sombra era simplesmente apagada. Isso causava que as sombras dos objetos poderiam desaparecer. Vamos fazer tudo dentro da verificação do sinalizador de redesenho:
//+------------------------------------------------------------------+ //| Redraw the object | //+------------------------------------------------------------------+ void CWinFormBase::Redraw(bool redraw) { //--- If the object type is less than the "Base WinForms object" or the object is not to be displayed, exit if(this.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_BASE || !this.Displayed()) return; //--- Get the "Shadow" object CShadowObj *shadow=this.GetShadowObj(); //--- If the redraw flag is set, if(redraw) { //--- completely redraw the object and save its new initial look this.Erase(this.m_array_colors_bg,this.Opacity(),this.m_gradient_v,this.m_gradient_c,redraw); this.Done(); //--- If the object has a shadow and the "Shadow" object exists, redraw it if(this.IsShadow() && shadow!=NULL) { //--- remove the previously drawn shadow, shadow.Erase(); //--- save the relative shadow coordinates, int x=shadow.CoordXRelative(); int y=shadow.CoordYRelative(); //--- redraw the shadow, shadow.Draw(0,0,shadow.Blur(),redraw); //--- restore relative shadow coordinates shadow.SetCoordXRelative(x); shadow.SetCoordYRelative(y); } } //--- otherwise, remove the object else this.Erase(); //--- Redraw all bound objects with the redraw flag for(int i=0;i<this.ElementsTotal();i++) { CWinFormBase *element=this.GetElement(i); if(element==NULL) continue; if(redraw) element.Redraw(redraw); } //--- If the redraw flag is set and if this is the main object the rest are bound to, //--- redraw the chart to display changes immediately if(this.IsMain() && redraw) ::ChartRedraw(this.ChartID()); } //+------------------------------------------------------------------+
No método que retorna a descrição da propriedade inteira do elemento, vamos adicionar um bloco de código para retornar descrições das novas propriedades do elemento gráfico:
//+------------------------------------------------------------------+ //| Return the description of the control integer property | //+------------------------------------------------------------------+ string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_INTEGER property,bool only_prop=false) { return ( property==CANV_ELEMENT_PROP_ID ? CMessage::Text(MSG_CANV_ELEMENT_PROP_ID)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : //---... //---... property==CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE ? CMessage::Text(MSG_CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_TOOLTIP_ICON ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_ICON)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_TOOLTIP_USE_FADING ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_USE_FADING)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : "" ); } //+------------------------------------------------------------------+
No método que retorna a descrição da propriedade de string do elemento, adicionaremos também o retorno das descrições das novas propriedades de string:
//+------------------------------------------------------------------+ //| Return the description of the control string property | //+------------------------------------------------------------------+ string CWinFormBase::GetPropertyDescription(ENUM_CANV_ELEMENT_PROP_STRING property,bool only_prop=false) { return ( property==CANV_ELEMENT_PROP_NAME_OBJ ? CMessage::Text(MSG_CANV_ELEMENT_PROP_NAME_OBJ)+": \""+this.GetProperty(property)+"\"" : property==CANV_ELEMENT_PROP_NAME_RES ? CMessage::Text(MSG_CANV_ELEMENT_PROP_NAME_RES)+": \""+this.GetProperty(property)+"\"" : property==CANV_ELEMENT_PROP_TEXT ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TEXT)+": \""+this.GetProperty(property)+"\"" : property==CANV_ELEMENT_PROP_DESCRIPTION ? CMessage::Text(MSG_CANV_ELEMENT_PROP_DESCRIPTION)+": \""+this.GetProperty(property)+"\"" : property==CANV_ELEMENT_PROP_TOOLTIP_TITLE ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_TITLE)+": \""+this.GetProperty(property)+"\"" : property==CANV_ELEMENT_PROP_TOOLTIP_TEXT ? CMessage::Text(MSG_CANV_ELEMENT_PROP_TOOLTIP_TEXT)+": \""+this.GetProperty(property)+"\"" : "" ); } //+------------------------------------------------------------------+
Como agora temos métodos para desenhar setas padrão, vamos adicionar sua chamada aos métodos DrawArrow() para desenhar setas nas classes dos objetos botões com setas.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowLeftButton.mqh:
//+------------------------------------------------------------------+ //| Draw the arrow | //+------------------------------------------------------------------+ void CArrowLeftButton::DrawArrow(void) { CGCnvElement::DrawArrowLeft(5,2,this.ArrowColor(),this.Opacity()); } //+------------------------------------------------------------------+
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowRightButton.mqh:
//+------------------------------------------------------------------+ //| Draw the arrow | //+------------------------------------------------------------------+ void CArrowRightButton::DrawArrow(void) { CGCnvElement::DrawArrowRight(6,2,this.ArrowColor(),this.Opacity()); } //+------------------------------------------------------------------+
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowUpButton.mqh:
//+------------------------------------------------------------------+ //| Draw the arrow | //+------------------------------------------------------------------+ void CArrowUpButton::DrawArrow(void) { CGCnvElement::DrawArrowUp(2,5,this.ArrowColor(),this.Opacity()); } //+------------------------------------------------------------------+
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\ArrowDownButton.mqh:
//+------------------------------------------------------------------+ //| Draw the arrow | //+------------------------------------------------------------------+ void CArrowDownButton::DrawArrow(void) { CGCnvElement::DrawArrowDown(2,6,this.ArrowColor(),this.Opacity()); } //+------------------------------------------------------------------+
Como podemos ver, em cada classe, chamamos o método para desenhar a seta desejada a partir da classe do elemento gráfico base, método esse que corresponde ao propósito do objeto da classe. Se a classe criar um botão de seta para a esquerda, o método de desenho de seta para a esquerda será chamado, para um botão de seta para a direita, o método de desenho de seta para a direita será chamado e assim por diante.
Vamos começar criando um objeto dica de ferramenta.
Classe do objeto WinForms ToolTip
No diretório da biblioteca \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\, criamos um novo arquivo Label.mqh da classe CLabel. A classe deve ser herdada da classedo objeto dica de ferramenta base e seu arquivo deve ser incluído no arquivo da classe criada:
//+------------------------------------------------------------------+ //| ToolTip.mqh | //| Copyright 2022, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\Helpers\HintBase.mqh" //+------------------------------------------------------------------+ //| Class of the base Hint object of the WForms controls | //+------------------------------------------------------------------+ class CToolTip : public CHintBase { }
Na seção privada da classe, declararemos um método que ajusta o tamanho da dica de ferramenta de acordo com os textos definidos para ela. Na seção protegida da classe, escreveremos métodos que desenham ícones de diferentes tipos e declararemos um construtor de classe protegido:
class CToolTip : public CHintBase { private: //--- Adjust a tooltip size according to a text size void CorrectSizeByTexts(void); protected: //--- Draw (1) tooltip, (2) icon, (3) Info, (4) Warning, (5) Error and (6) User virtual void DrawHint(const int shift); void DrawIcon(void); void DrawIconInfo(void) { CGCnvElement::DrawIconInfo(3,1,this.Opacity()); } void DrawIconWarning(void) { CGCnvElement::DrawIconWarning(3,1,this.Opacity()); } void DrawIconError(void) { CGCnvElement::DrawIconError(3,1,this.Opacity()); } virtual void DrawIconUser(void) { return; } //--- Protected constructor with object type, chart ID and subwindow CToolTip(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public:
Basicamente, os métodos que desenham ícones Info, Warning e Error chamam os métodos correspondentes da classe base do elemento gráfico que escrevemos acima, e o método virtual que desenha um ícone personalizado não faz nada - ele deve ser substituído em classes herdadas caso você precise alterar independentemente a funcionalidade deste objeto.
Na seção pública da classe, vamos escrever métodos para definir e retornar as propriedades que adicionamos hoje para este objeto. Vamos declarar um construtor de classe paramétrica e métodos para exibir, redesenhar, limpar, desenhar uma borda e inicializar o objeto:
public: //--- (1) Set and (2) return the tooltip display delay void SetInitialDelay(const long delay) { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,delay); } long InitialDelay(void) { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY); } //--- (1) Set and (2) return the tooltip display duration void SetAutoPopDelay(const long delay) { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,delay); } long AutoPopDelay(void) { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY); } //--- (1) Set and (2) return the delay in a display of a new tooltip of one element void SetReshowDelay(const long delay) { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,delay); } long ReshowDelay(void) { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY); } //--- (1) Set and (2) return the flag for displaying a tooltip in an inactive window void SetShowAlways(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,flag); } bool ShowAlways(void) { return (bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS); } //--- (1) Set and (2) return the type of the icon displayed in a tooltip void SetIcon(const ENUM_CANV_ELEMENT_TOOLTIP_ICON ico) { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,ico); } ENUM_CANV_ELEMENT_TOOLTIP_ICON Icon(void) { return (ENUM_CANV_ELEMENT_TOOLTIP_ICON)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON); } //--- (1) Set and (2) return the cloud tooltip flag void SetBalloon(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,flag); } bool Balloon(void) { return (bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON); } //--- (1) Set and (2) return the flag of fading when showing/hiding a tooltip void SetUseFading(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,flag); } bool UseFading(void) { return (bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING); } //--- (1) Set and (2) return the Tooltip title void SetTitle(const string header); string Title(void) { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE); } //--- (1,2) Set the Tooltip text virtual void SetTooltipText(const string text); virtual void SetText(const string text) { this.SetTooltipText(text); } //--- Constructor CToolTip(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); //--- Display the element virtual void Show(void); //--- Redraw the object virtual void Redraw(bool redraw); //--- Clear the element filling it with color and opacity virtual void Erase(const color colour,const uchar opacity,const bool redraw=false); //--- Clear the element with a gradient fill virtual void Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false); //--- Draw the hint frame virtual void DrawFrame(void); //--- Initialize the variables virtual void Initialize(void); }; //+------------------------------------------------------------------+
Vamos dar uma olhada mais de perto nos métodos declarados.
Construtor protegido com especificação do tipo de objeto, identificador do gráfico e da subjanela:
//+------------------------------------------------------------------+ //| Protected constructor with an object type, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CToolTip::CToolTip(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CHintBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { //--- Set the specified graphical element type for the object and assign the library object type to the current object this.SetTypeElement(type); this.m_type=OBJECT_DE_TYPE_GWF_COMMON; this.Initialize(); } //+------------------------------------------------------------------+
O tipo do elemento gráfico criado e outros parâmetros do objeto são passados para o construtor, que são definidos na classe pai na linha de inicialização. O corpo da classe define o tipo do elemento gráfico passado para o método e o tipo do objeto gráfico da biblioteca, e o método é chamado para definir todos os parâmetros padrão do objeto.
Construtor paramétrico:
//+------------------------------------------------------------------+ //| Constructor indicating the main and base objects, | //| chart ID and subwindow | //+------------------------------------------------------------------+ CToolTip::CToolTip(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h) : CHintBase(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h) { this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_TOOLTIP); this.m_type=OBJECT_DE_TYPE_GWF_COMMON; this.Initialize(); } //+------------------------------------------------------------------+
Aqui é exatamente o mesmo que no construtor protegido, mas o tipo do elemento gráfico é codificado como ToolTip.
Método de inicialização de variável privada
//+------------------------------------------------------------------+ //| Initialize the variables | //+------------------------------------------------------------------+ void CToolTip::Initialize(void) { this.SetBackgroundColor(CLR_DEF_CONTROL_HINT_BACK_COLOR,true); this.SetBorderColor(CLR_DEF_CONTROL_HINT_BORDER_COLOR,true); this.SetForeColor(CLR_DEF_CONTROL_HINT_FORE_COLOR,true); this.SetDisplayed(false); this.SetBorderSizeAll(1); this.SetBorderStyle(FRAME_STYLE_SIMPLE); this.SetShadow(true); this.DrawShadow(2,2,CLR_DEF_SHADOW_COLOR,CLR_DEF_SHADOW_OPACITY,DEF_SHADOW_BLUR); this.SetOpacity(255,false); this.SetTitle(""); this.SetTooltipText(""); this.SetInitialDelay(500); this.SetAutoPopDelay(5000); this.SetReshowDelay(100); this.SetShowAlways(false); this.SetIcon(CANV_ELEMENT_TOOLTIP_ICON_NONE); this.SetBalloon(false); this.SetUseFading(true); this.SetTextAlign(ANCHOR_LEFT_UPPER); this.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP); this.SetFont(DEF_FONT,DEF_FONT_SIZE,FW_NORMAL); this.SetDisplayed(false); this.Hide(); } //+------------------------------------------------------------------+
Isso define todas as propriedades do objeto dica de ferramenta que ele deve ter imediatamente após sua criação.
Método que redesenha o objeto:
//+------------------------------------------------------------------+ //| Redraw the object | //+------------------------------------------------------------------+ void CToolTip::Redraw(bool redraw) { //--- If the element should not be displayed (hidden inside another control), leave if(!this.Displayed() || this.Text()=="") return; //--- Get the "Shadow" object CShadowObj *shadow=this.GetShadowObj(); //--- If the redraw flag is set, if(redraw) { //--- completely redraw the object and save its new initial look this.Erase(this.m_array_colors_bg,this.Opacity(),this.m_gradient_v,this.m_gradient_c,true); this.Done(); //--- If the object has a shadow and the "Shadow" object exists, redraw it if(this.IsShadow() && shadow!=NULL) { //--- remove the previously drawn shadow, shadow.Erase(); //--- save the relative shadow coordinates, int x=shadow.CoordXRelative(); int y=shadow.CoordYRelative(); //--- redraw the shadow, shadow.Draw(0,0,shadow.Blur(),true); //--- restore relative shadow coordinates shadow.SetCoordXRelative(x); shadow.SetCoordYRelative(y); } } //--- otherwise, erase the object and its shadow else { CGCnvElement::Erase(); if(this.IsShadow() && shadow!=NULL) shadow.Erase(); } } //+------------------------------------------------------------------+
O método utiliza uma lógica descrita nos comentários do código. Aqui, quase tudo é exatamente o mesmo que no método de mesmo nome na classe do objeto WinForms base CWinFormBase, exceto para redesenhar os objetos vinculados e o gráfico.
Métodos para limpar um elemento e pintá-lo com uma cor de fundo:
//+------------------------------------------------------------------+ //| Clear the element filling it with color and opacity | //+------------------------------------------------------------------+ void CToolTip::Erase(const color colour,const uchar opacity,const bool redraw=false) { //--- Fill the element having the specified color and the redrawing flag CGCnvElement::EraseNoCrop(colour,opacity,false); //--- If the object has a frame, draw it if(this.BorderStyle()!=FRAME_STYLE_NONE) this.DrawFrame(); //--- Draw a hint this.DrawHint(0); //--- Update the element having the specified redrawing flag this.Update(redraw); } //+------------------------------------------------------------------+ //| Clear the element with a gradient fill | //+------------------------------------------------------------------+ void CToolTip::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false) { //--- Fill the element having the specified color array and the redrawing flag CGCnvElement::EraseNoCrop(colors,opacity,vgradient,cycle,false); //--- If the object has a frame, draw it if(this.BorderStyle()!=FRAME_STYLE_NONE) this.DrawFrame(); //--- Draw a hint this.DrawHint(0); //--- Update the element having the specified redrawing flag this.Update(redraw); } //+------------------------------------------------------------------+
Os métodos são idênticos aos métodos de mesmo nome de outros elementos gráficos da biblioteca e, em particular, aos métodos do objeto pai. Se nenhuma outra alteração precisar ser feita aqui, eles serão removidos da classe.
Método que desenha a borda do elemento:
//+------------------------------------------------------------------+ //| Draw the element border | //+------------------------------------------------------------------+ void CToolTip::DrawFrame(void) { //--- If the element should not be displayed (hidden inside another control), leave if(!this.Displayed() || this.Text()=="") return; //--- Draw a rectangle along the object edges this.DrawRectangle(0,0,this.Width()-1,this.Height()-1,this.BorderColor(),this.Opacity()); } //+------------------------------------------------------------------+
A lógica do método é comentada no código. A diferença em relação ao método da classe pai aqui está apenas na verificação da necessidade de exibir este elemento.
Método que renderiza o elemento:
//+------------------------------------------------------------------+ //| Show the element | //+------------------------------------------------------------------+ void CToolTip::Show(void) { //--- If the element should not be displayed (hidden inside another control), leave if(!this.Displayed() || this.TooltipText()=="") return; //--- Display the object CGCnvElement::Show(); //--- Get the "Shadow" object CShadowObj *shadow=this.GetShadowObj(); //--- If the object has a shadow and the "Shadow" object exists, display the shadow if(this.IsShadow() && shadow!=NULL) { shadow.Show(); this.BringToTop(); } //--- Redraw the object this.Redraw(true); } //+------------------------------------------------------------------+
O método verifica se o objeto possui sombra e, se tiver, ela também precisará ser exibida. Neste caso, após a exibição da sombra, o próprio objeto deve ser movido para o primeiro plano, pois após a exibição da sombra, o objeto ficará sob ela.
Método que define o texto para o ToolTip:
//+------------------------------------------------------------------+ //| Set the Tooltip text | //+------------------------------------------------------------------+ void CToolTip::SetTooltipText(const string text) { CGCnvElement::SetTooltipText(text); CWinFormBase::SetText(text); this.CorrectSizeByTexts(); } //+------------------------------------------------------------------+
Aqui, primeiro definimos o texto da dica de ferramenta na nova propriedade do objeto e, em seguida, definimos o mesmo texto na propriedade "texto do objeto", pois essas propriedades podem ser consideradas idênticas para o objeto dica de ferramenta. Em seguida, o método para ajustar o tamanho do objeto é chamado de acordo com os textos do título e da dica de ferramenta definidos, vamos considerá-lo a seguir.
Método que define o texto do título para o ToolTip:
//+------------------------------------------------------------------+ //| Set the title text for Tooltip | //+------------------------------------------------------------------+ void CToolTip::SetTitle(const string header) { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,header); this.CorrectSizeByTexts(); } //+------------------------------------------------------------------+
Aqui, primeiro definimos o texto passado para o método como uma nova propriedade de string do objeto e, em seguida, chamamos o método de ajuste de tamanho do objeto.
Método que ajusta o tamanho do tooltip de acordo com o tamanho dos textos:
//+------------------------------------------------------------------+ //| Adjust tooltip size to fit texts | //+------------------------------------------------------------------+ void CToolTip::CorrectSizeByTexts(void) { //--- If the tooltip text is not set for the object, leave if(this.TooltipText()=="" || this.TooltipText()==NULL) return; //--- Declare variables for the width and height of the object int w=this.Width(); int h=this.Height(); //--- Declare variables for the width and height of the title and tooltip texts int w1=0; int h1=0; int w2=w; int h2=h; //--- If the header text is set, get and adjust its size if(this.Title()!="" && this.Title()!=NULL) { this.TextSize(this.Title(),w1,h1); if(w1<6) w1=6; if(h1<19) h1=19; } //--- If the tooltip text is set, get and adjust its size this.TextSize(this.Text(),w2,h2); if(w2<6) w2=6; if(h2<19) h2=19; //--- Calculate the total size of the tooltip w=fmax(w1,w2); h=h1+h2; //--- Set the size of the object in accordance with the calculated ones this.Resize(w+12+(this.Icon()>CANV_ELEMENT_TOOLTIP_ICON_NONE && this.Icon()<=CANV_ELEMENT_TOOLTIP_ICON_USER ? 16 : 0),h,false); } //+------------------------------------------------------------------+
O método utiliza uma lógica descrita nos comentários do código. Resumindo, se nenhum texto de dica de ferramenta for definido para o objeto, nenhuma dica de ferramenta será exibida. Independentemente de se o objeto tem um texto de título ou não. E se a dica não for exibida, seu tamanho não precisa ser alterado. Portanto, em tal situação, simplesmente abandonamos o método. E então, se o texto da dica de ferramenta estiver definido, precisamos obter os tamanhos do título e do texto da dica de ferramenta. Se a largura do texto resultante for menor que 6, a largura do objeto será igual a 6, se a altura for menor que 19, a altura do objeto deverá ser 19. Em seguida, determinamos a maior largura dos dois textos e a usamos como a largura do objeto. E a altura do objeto é igual à soma das alturas dos dois textos. Ao redimensionar, levamos em consideração um recuo de seis pixels em relação ao texto da borda esquerda. Assim, o recuo à direita também deve ser igual a seis pixels. Portanto, adicionamos 12 pixels à largura calculada e verificamos a condição da presença do ícone. Se o ícone precisar ser desenhado, adicionamos 16 pixels da largura do ícone à largura do objeto. Como resultado, obtemos o tamanho correto do objeto, onde os textos do título e da descrição, e do ícone, se disponível, parecerão harmoniosos.
Método que desenha a dica:
//+------------------------------------------------------------------+ //| Draw a hint | //+------------------------------------------------------------------+ void CToolTip::DrawHint(const int shift) { int y=3; int x=6+(this.Icon()>CANV_ELEMENT_TOOLTIP_ICON_NONE ? 16 : 0); this.DrawIcon(); if(this.Title()!="" && this.Title()!=NULL) { this.SetFont(DEF_FONT,DEF_FONT_SIZE,FW_BLACK); this.Text(x,y,this.Title(),this.ForeColor(),this.Opacity(),this.TextAnchor()); this.SetFont(DEF_FONT,DEF_FONT_SIZE,FW_NORMAL); y+=this.TextHeight(this.Title())+4; } this.Text(x,y,this.Text(),this.ForeColor(),this.Opacity(),this.TextAnchor()); } //+------------------------------------------------------------------+
Definimos para o texto um recuo vertical de três pixels e um recuo horizontal de seis pixels. Se for necessário desenhar um ícone, adicionamos 16 pixels de largura ao recuo horizontal. Se a dica de ferramenta tiver um título, definimos a fonte como "bold", exibimos o texto do título, restauramos o peso da fonte ao normal e adicionamos a altura do texto do título mais 4 pixels à coordenada vertical. Ao terminar, exibimos o texto da dica. Se não houver título, o texto da dica de ferramenta será exibido na coordenada Y inicial e, se houver título, na calculada.
Método de desenho do ícone:
//+------------------------------------------------------------------+ //| Draw an icon | //+------------------------------------------------------------------+ void CToolTip::DrawIcon(void) { switch(this.Icon()) { case CANV_ELEMENT_TOOLTIP_ICON_INFO : this.DrawIconInfo(); break; case CANV_ELEMENT_TOOLTIP_ICON_WARNING : this.DrawIconWarning(); break; case CANV_ELEMENT_TOOLTIP_ICON_ERROR : this.DrawIconError(); break; case CANV_ELEMENT_TOOLTIP_ICON_USER : this.DrawIconUser(); break; //--- Icon None default: break; } } //+------------------------------------------------------------------+
Dependendo do tipo de ícone definido para o objeto, chamamos o método para desenhar a imagem correspondente.
Por enquanto, isso é tudo o que é necessário para que esta classe funcione. Além disso, modificaremos a classe para obter seus resultados de trabalho.
Vamos refinar o restante das classes da biblioteca para poder trabalhar com o novo objeto.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\Form.mqh da classe de objeto forma, na seção pública, declararemos métodos virtuais para definir o sinalizador de exibição de controle não oculto e de definição de opacidade do elemento:
//--- Update the coordinates (shift the canvas) virtual bool Move(const int x,const int y,const bool redraw=false); //--- Set the priority of a graphical object for receiving the event of clicking on a chart virtual bool SetZorder(const long value,const bool only_prop); //--- Set the object above all virtual void BringToTop(void); //--- Set the flag for displaying a non-hidden control virtual void SetDisplayed(const bool flag); //--- Set the element opacity virtual void SetOpacity(const uchar value,const bool redraw=false); //--- Event handler virtual void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
No mesmo local, na seção pública, declararemos métodos para trabalhar com o objeto dica de ferramenta anexado e para definir seu texto:
//--- Add a new attached element bool AddNewElement(CGCnvElement *obj,const int x,const int y); //--- (1) Attach, return (2) the attached ToolTip object, (3) by description bool AddTooltip(CForm *tooltip); CForm *GetTooltip(void); CForm *GetTooltipByDescription(const string descript); //--- Set the text for Tooltip virtual void SetTooltipText(const string text); //--- Draw an object shadow void DrawShadow(const int shift_x,const int shift_y,const color colour,const uchar opacity=127,const uchar blur=DEF_SHADOW_BLUR);
Vamos corrigir o método que cria um novo objeto gráfico:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CForm::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *element=NULL; //--- Depending on the created object type, switch(type) { //--- create a graphical element object case GRAPH_ELEMENT_TYPE_ELEMENT : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break; //--- create a form object case GRAPH_ELEMENT_TYPE_FORM : element=new CForm(type,this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default: break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); else { element.SetMovable(movable); element.SetCoordXRelative(element.CoordX()-this.CoordX()); element.SetCoordYRelative(element.CoordY()-this.CoordY()); } return element; } //+------------------------------------------------------------------+
Se o objeto não for criado, log exibirá uma entrada sobre isso. E se depois houver uma chamada ao ponteiro de um objeto não criado, o que poderia causar um erro crítico. Portanto, vamos colocar o bloco para definição dos parâmetros do objeto criado em else {}.
Cada objeto WinForms deve ser capaz de anexar a si mesmo um ponteiro para um objeto dica de ferramenta criado anteriormente. Para que um objeto "entenda" que tal dica já foi anexada a ele, ele deve distingui-las pela descrição registrada no objeto dica. Assim, poderemos criar vários objetos tooltip no objeto contêiner, definindo uma descrição única para cada um deles. Então podemos obter um ponteiro para qualquer outro objeto anexado àquele em que as dicas foram criadas e atribuir a dica desejada a esse objeto com base na sua descrição. Para fazer isso, cada objeto WinForms deve ter métodos que permitam anexar um objeto com o tipo CForm e seus descendentes. Já declaramos tais métodos na classe do objeto forma. Vamos ver como implementá-los.
Método que cria um objeto ToolTip anexado:
//+------------------------------------------------------------------+ //| Create the attached ToolTip object | //+------------------------------------------------------------------+ bool CForm::AddTooltip(CForm *tooltip) { //--- If the pointer to an empty object is passed or the object type is not equal to Tooltip, report an error and return 'false' if(tooltip==NULL || tooltip.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_WF_TOOLTIP) { CMessage::ToLog(DFUN,MSG_GRAPH_ELM_COLLECTION_ERR_EMPTY_OBJECT); return false; } //--- If the list of attached objects already contains the Tooltip object with the same description as the object passed to the method - //--- inform of that in the journal and return 'false' if(this.GetTooltipByDescription(tooltip.Description())!=NULL) { ::Print(DFUN,this.TypeElementDescription()+": ",CMessage::Text(MSG_FORM_TOOLTIP_OBJ_ALREADY_EXISTS),": ",tooltip.Name(),", Description: \"",tooltip.Description(),"\""); return false; } //--- If it was not possible to add the Tooltip object to the list of attached objects, report an error and return 'false' if(!this.m_list_elements.Add(tooltip)) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST),": ",tooltip.NameObj()); return false; } //--- If the coordinates of the object added to the list are changed, set the Tooltip relative coordinates if(tooltip.Move(this.CoordX()+1,this.CoordY()+1)) { tooltip.SetCoordXRelative(tooltip.CoordX()-this.CoordX()); tooltip.SetCoordYRelative(tooltip.CoordY()-this.CoordY()); } //--- Set this object as the base object for the Tooltip object and return 'true' tooltip.SetBase(this.GetObject()); return true; } //+------------------------------------------------------------------+
A lógica do método é descrita nos comentários ao código. O ponteiro para o objeto dica de ferramenta é passado para o método. Se um objeto desse tipo com exatamente a mesma descrição já estiver anexado ao objeto, consideramos que essa é a mesma dica. Relatamos um erro e retornamos false. Em seguida, à lista de objetos anexados anexamos o ponteiro para o objeto dica passado ao método. Nesse caso, o próprio objeto pode não saber nada sobre o tipo CToolTip, mas como esse tipo é um herdeiro da classe CForm, passá-lo para o método com o tipo CForm está correto. E depois, na classe coleção onde os objetos pop-up são processados, podemos pegar esse objeto da lista e trabalhar com ele como objeto CToolTip. No final do método, um ponteiro para o objeto atual é gravado no objeto de dica para que ele se torne a base do objeto de dica de ferramenta. Como os objetos dica podem ser criados em outros objetos e, em seguida, anexados a terceiros, a entrada sobre o objeto base nos ajudará a determinar a qual objeto ele é atribuído como dica. É o objeto base que é o objeto ao qual esta dica de ferramenta é atribuída.
Método que retorna o objeto ToolTip anexado:
//+------------------------------------------------------------------+ //| Return the attached ToolTip object | //+------------------------------------------------------------------+ CForm *CForm::GetTooltip(void) { CForm *obj=NULL; for(int i=this.ElementsTotal()-1;i>WRONG_VALUE;i--) { obj=this.GetElement(i); if(obj!=NULL && obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP) break; } return obj; } //+------------------------------------------------------------------+
Aqui, no loop sobre os objetos anexados, obtemos o próximo objeto pelo índice do loop. Se este objeto não estiver vazio e ser do tipo CToolTip, interrompemos o loop. Ao final do loop, retornamos o ponteiro armazenado na variável obj.
Um objeto pode receber várias dicas com descrições diferentes. Para obter um ponteiro para o objeto dica de ferramenta desejado, foi criado um método que retorna o objeto ToolTip anexado conforme a descrição:
//+------------------------------------------------------------------+ //| Return the attached ToolTip object by description | //+------------------------------------------------------------------+ CForm *CForm::GetTooltipByDescription(const string descript) { CForm *obj=NULL; for(int i=this.ElementsTotal()-1;i>WRONG_VALUE;i--) { obj=this.GetElement(i); if(obj!=NULL && obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP && obj.Description()==descript) break; } return obj; } //+------------------------------------------------------------------+
O método recebe o texto da descrição necessário para localizar o objeto dica. Em um loop sobre lista de objetos anexados, obtemos o próximo objeto e, se não estiver vazio, seu tipo é CToolTip e a descrição é igual à desejada, interrompemos o loop. Ao final do loop, retornamos um ponteiro para o objeto encontrado (ou vazio).
Método que define o texto para o ToolTip:
//+------------------------------------------------------------------+ //| Set the Tooltip text | //+------------------------------------------------------------------+ void CForm::SetTooltipText(const string text) { CGCnvElement::SetTooltipText(text); CForm *tooltip=this.GetTooltip(); if(tooltip!=NULL) tooltip.SetTooltipText(text); } //+------------------------------------------------------------------+
Primeiro, definimos o texto da dica de ferramenta no próprio objeto. Em seguida, obtemos um ponteiro para o objeto dica de ferramenta anexado e definimos o texto da dica de ferramenta passado para o método. Se o objeto tiver apenas uma dica de ferramenta, o método definirá o mesmo texto para esse objeto e a dica de ferramenta atribuída a ele.
Método virtual que define o sinalizador para exibir um controle não oculto:
//+------------------------------------------------------------------+ //| Set the flag for displaying a non-hidden control | //+------------------------------------------------------------------+ void CForm::SetDisplayed(const bool flag) { //--- Set the display flag for the object CGCnvElement::SetDisplayed(flag); //--- If the shadow use flag is set and the shadow object has been created, //--- set the display flag for the shadow object if(this.m_shadow && this.m_shadow_obj!=NULL) this.m_shadow_obj.SetDisplayed(flag); } //+------------------------------------------------------------------+
Para um objeto que pode ter uma sombra projetada sobre outros objetos, é necessário definir o sinalizador de exibição não apenas para o próprio objeto, mas também para o objeto de sombra. Caso contrário, a sombra "viverá sua própria vida", independentemente do objeto. Portanto, aqui primeiro definimos o sinalizador de exibição para o próprio objeto e, se houver uma sombra, para ele.
Escrevemos um novo método virtual que define a opacidade do elemento:
//+------------------------------------------------------------------+ //| Set the element opacity | //+------------------------------------------------------------------+ void CForm::SetOpacity(const uchar value,const bool redraw=false) { //--- Set the object opacity value CGCnvElement::SetOpacity(value,redraw); //--- If the shadow use flag is set and the shadow object has been created, //--- set the opacity value for the shadow object if(this.m_shadow && this.m_shadow_obj!=NULL) this.m_shadow_obj.SetOpacity(value,redraw); } //+------------------------------------------------------------------+
É o mesmo, primeiro definimos o valor de opacidade para o próprio objeto e, se tiver um objeto sombra, para ela.
Para trabalhar com o novo controle, precisamos incluir seu arquivo ao arquivo de classe do objeto forma.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh, adicionamos a inclusão do arquivo de classe CToolTip:
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Container.mqh" #include "..\Helpers\TabField.mqh" #include "..\Helpers\ArrowUpButton.mqh" #include "..\Helpers\ArrowDownButton.mqh" #include "..\Helpers\ArrowLeftButton.mqh" #include "..\Helpers\ArrowRightButton.mqh" #include "..\Helpers\ArrowUpDownBox.mqh" #include "..\Helpers\ArrowLeftRightBox.mqh" #include "..\Helpers\HintMoveLeft.mqh" #include "..\Helpers\HintMoveRight.mqh" #include "..\Helpers\HintMoveUp.mqh" #include "..\Helpers\HintMoveDown.mqh" #include "GroupBox.mqh" #include "TabControl.mqh" #include "SplitContainer.mqh" #include "..\..\WForms\Common Controls\ListBox.mqh" #include "..\..\WForms\Common Controls\CheckedListBox.mqh" #include "..\..\WForms\Common Controls\ButtonListBox.mqh" #include "..\..\WForms\Common Controls\ToolTip.mqh" //+------------------------------------------------------------------+ //| Panel object class of WForms controls | //+------------------------------------------------------------------+
No método que cria um novo objeto gráfico, inserimos uma linha para criar um novo objeto dica de ferramenta:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CPanel::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *element=NULL; switch(type) { case GRAPH_ELEMENT_TYPE_ELEMENT : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break; case GRAPH_ELEMENT_TYPE_FORM : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CONTAINER : element=new CContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_GROUPBOX : element=new CGroupBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_PANEL : element=new CPanel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LABEL : element=new CLabel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CHECKBOX : element=new CCheckBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON : element=new CRadioButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_BUTTON : element=new CButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LIST_BOX : element=new CListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM : element=new CListBoxItem(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX : element=new CCheckedListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX : element=new CButtonListBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER : element=new CTabHeader(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD : element=new CTabField(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL : element=new CTabControl(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON : element=new CArrowButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP : element=new CArrowUpButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN : element=new CArrowDownButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT : element=new CArrowLeftButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT : element=new CArrowRightButton(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER : element=new CSplitContainer(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_SPLITTER : element=new CSplitter(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_BASE : element=new CHintBase(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT : element=new CHintMoveLeft(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT : element=new CHintMoveRight(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TOOLTIP : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default : break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); return element; } //+------------------------------------------------------------------+
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh, no método que define os parâmetros do objeto anexado, escreveremos um bloco de código para definir os parâmetros do objeto dica de ferramenta recém-criado:
//+------------------------------------------------------------------+ //| Set parameters for the attached object | //+------------------------------------------------------------------+ void CContainer::SetObjParams(CWinFormBase *obj,const color colour) { //--- Set the text color of the object to be the same as that of the base container obj.SetForeColor(this.ForeColor(),true); //--- If the created object is not a container, set the same group for it as the one for its base object if(obj.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_CONTAINER || obj.TypeGraphElement()>GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER) obj.SetGroup(this.Group()); //--- Depending on the object type switch(obj.TypeGraphElement()) { //--- For the Container, Panel and GroupBox WinForms objects //---... //---... //--- For ToolTip WinForms object case GRAPH_ELEMENT_TYPE_WF_TOOLTIP : obj.SetBackgroundColor(CLR_DEF_CONTROL_HINT_BACK_COLOR,true); obj.SetBorderColor(CLR_DEF_CONTROL_HINT_BORDER_COLOR,true); obj.SetForeColor(CLR_DEF_CONTROL_HINT_FORE_COLOR,true); obj.SetBorderStyle(FRAME_STYLE_SIMPLE); obj.SetOpacity(0,false); obj.SetDisplayed(false); obj.Hide(); break; default: break; } obj.Crop(); } //+------------------------------------------------------------------+
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh do objeto GroupBox, em seu método de criação de novo objeto gráfico, adicionamos também uma linha para criar um objeto dica de ferramenta:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CGroupBox::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *element=NULL; switch(type) { case GRAPH_ELEMENT_TYPE_ELEMENT : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break; case GRAPH_ELEMENT_TYPE_FORM : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; //---... //---... case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TOOLTIP : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default : break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); return element; } //+------------------------------------------------------------------+
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\TabControl.mqh da classe do objeto TabControl, precisamos adicionar métodos para obter objetos botões com setas que são usadas para rolar a fileira de títulos. Sem obter ponteiros para esses objetos auxiliares, não poderemos atribuir dicas de ferramentas a eles.
Na seção privada da classe, declararemos métodos para obter ponteiros para objetos botões:
//--- Return the list of (1) headers, (2) tab fields, the pointer to the (3) up-down and (4) left-right button objects CArrayObj *GetListHeaders(void) { return this.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER); } CArrayObj *GetListFields(void) { return this.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_TAB_FIELD); } CArrowUpDownBox *GetArrUpDownBox(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,0); } CArrowLeftRightBox *GetArrLeftRightBox(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,0); } //--- Return the pointer to the (1) up, (2) down, (3) left and (4) right arrow button CArrowUpButton *GetArrowUpButton(void); CArrowDownButton *GetArrowDownButton(void); CArrowLeftButton *GetArrowLeftButton(void); CArrowRightButton*GetArrowRightButton(void); //--- Return the pointer to the (1) last and (2) first visible tab header CTabHeader *GetLastHeader(void) { return this.GetTabHeader(this.TabPages()-1); } CTabHeader *GetFirstVisibleHeader(void); //--- Set the tab as selected
Na seção pública, declararemos métodos para definir dicas de ferramentas para objetos botões com setas:
//--- Set the tab specified by index to selected/not selected void Select(const int index,const bool flag); //--- Returns the (1) index, (2) the pointer to the selected tab int SelectedTabPageNum(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER);} CWinFormBase *SelectedTabPage(void) { return this.GetTabField(this.SelectedTabPageNum()); } //--- Add Tooltip to the (1) up, (2) down, (3) left and (4) right arrow button bool AddTooltipToArrowUpButton(CForm *tooltip); bool AddTooltipToArrowDownButton(CForm *tooltip); bool AddTooltipToArrowLeftButton(CForm *tooltip); bool AddTooltipToArrowRightButton(CForm *tooltip); //--- Set the object above all virtual void BringToTop(void); //--- Show the control virtual void Show(void); //--- Event handler
No método que cria um novo objeto gráfico, inserimos uma linha para criar um novo objeto dica de ferramenta:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CTabControl::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *element=NULL; switch(type) { case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER : element=new CTabHeader(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD : element=new CTabField(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TOOLTIP : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default : break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); return element; } //+------------------------------------------------------------------+
Métodos que retornam ponteiros para botões com setas:
//+------------------------------------------------------------------+ //| Return the pointer to the up arrow button | //+------------------------------------------------------------------+ CArrowUpButton *CTabControl::GetArrowUpButton(void) { CArrowUpDownBox *box=this.GetArrUpDownBox(); return(box!=NULL ? box.GetArrowUpButton() : NULL); } //+------------------------------------------------------------------+ //| Return the pointer to the down arrow button | //+------------------------------------------------------------------+ CArrowDownButton *CTabControl::GetArrowDownButton(void) { CArrowUpDownBox *box=this.GetArrUpDownBox(); return(box!=NULL ? box.GetArrowDownButton() : NULL); } //+------------------------------------------------------------------+ //| Return the pointer to the left arrow button | //+------------------------------------------------------------------+ CArrowLeftButton *CTabControl::GetArrowLeftButton(void) { CArrowLeftRightBox *box=this.GetArrLeftRightBox(); return(box!=NULL ? box.GetArrowLeftButton() : NULL); } //+------------------------------------------------------------------+ //| Return the pointer to the right arrow button | //+------------------------------------------------------------------+ CArrowRightButton *CTabControl::GetArrowRightButton(void) { CArrowLeftRightBox *box=this.GetArrLeftRightBox(); return(box!=NULL ? box.GetArrowRightButton() : NULL); } //+------------------------------------------------------------------+
Todos os métodos são idênticos entre si. Primeiro, obtemos um ponteiro para o objeto correspondente que contém um par de botões esquerdo-direito ou para cima-baixo. E já desses objetos recebemos e retornamos o ponteiro para o botão desejado - para cima, para baixo, para a esquerda ou para a direita.
Métodos que adicionam um objeto ToolTip a um botão de seta para cima, para baixo, para a esquerda e para a direita:
//+------------------------------------------------------------------+ //| Add ToolTip to the up arrow button | //+------------------------------------------------------------------+ bool CTabControl::AddTooltipToArrowUpButton(CForm *tooltip) { CArrowUpButton *butt=this.GetArrowUpButton(); return(butt!=NULL ? butt.AddTooltip(tooltip) : false); } //+------------------------------------------------------------------+ //| Add ToolTip to the down arrow button | //+------------------------------------------------------------------+ bool CTabControl::AddTooltipToArrowDownButton(CForm *tooltip) { CArrowDownButton *butt=this.GetArrowDownButton(); return(butt!=NULL ? butt.AddTooltip(tooltip) : false); } //+------------------------------------------------------------------+ //| Add ToolTip to the left arrow button | //+------------------------------------------------------------------+ bool CTabControl::AddTooltipToArrowLeftButton(CForm *tooltip) { CArrowLeftButton *butt=this.GetArrowLeftButton(); return(butt!=NULL ? butt.AddTooltip(tooltip) : false); } //+------------------------------------------------------------------+ //| Add ToolTip to the right arrow button | //+------------------------------------------------------------------+ bool CTabControl::AddTooltipToArrowRightButton(CForm *tooltip) { CArrowRightButton *butt=this.GetArrowRightButton(); return(butt!=NULL ? butt.AddTooltip(tooltip) : false); } //+------------------------------------------------------------------+
Os métodos são idênticos entre si. Primeiro, obtemos um ponteiro para o botão correspondente usando os métodos acima e, em seguida, definimos o ponteiro para o objeto dica de ferramenta passado ao método para o objeto recebido. Ao receber com sucesso o ponteiro para o objeto botão e adicionar o ponteiro ao objeto dica de ferramenta a ele, o método retorna true, caso contrário, false.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\TabField.mqh da classe do objeto TabField, adicionamos uma linha ao método que cria um novo objeto gráfico para criar um novo objeto dica de ferramenta:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CTabField::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *element=NULL; switch(type) { case GRAPH_ELEMENT_TYPE_ELEMENT : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break; case GRAPH_ELEMENT_TYPE_FORM : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; //---... //---... case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TOOLTIP : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default : break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); return element; } //+------------------------------------------------------------------+
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\SplitContainer.mqh da classe do objeto SplitContainer, declararemos um método para definir a dica para o objeto separador:
//--- Return the pointer to the separator CSplitter *GetSplitter(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_SPLITTER,0); } //--- Return a pointer to the (1) "Left shift", (1) "Right shift", (1) "Up shift" and (1) "Down shift" hint objects CHintMoveLeft *GetHintMoveLeft(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,0); } CHintMoveRight *GetHintMoveRight(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,0); } CHintMoveUp *GetHintMoveUp(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,0); } CHintMoveDown *GetHintMoveDown(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,0); } //--- Add Tooltip to the separator bool AddTooltipToSplitter(CForm *tooltip); //--- (1) set and (2) return the minimum possible size of the panel 1 and 2
No método que cria um novo objeto gráfico, inserimos uma linha para criar um novo objeto dica de ferramenta:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CSplitContainer::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *element=NULL; switch(type) { case GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL: element=new CSplitContainerPanel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h);break; case GRAPH_ELEMENT_TYPE_WF_SPLITTER : element=new CSplitter(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_BASE : element=new CHintBase(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT : element=new CHintMoveLeft(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT : element=new CHintMoveRight(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TOOLTIP : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default : break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); return element; } //+------------------------------------------------------------------+
Método que adiciona o ToolTip ao separador:
//+------------------------------------------------------------------+ //| Add ToolTip to the separator | //+------------------------------------------------------------------+ bool CSplitContainer::AddTooltipToSplitter(CForm *tooltip) { CSplitter *obj=this.GetSplitter(); return(obj!=NULL ? obj.AddTooltip(tooltip) : false); } //+------------------------------------------------------------------+
Aqui, obtemos um ponteiro para o objeto separador e retornamos o resultado da adição do ponteiro para o objeto dica de ferramenta ao objeto separador.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\SplitContainerPanel.mqh de um objeto da classe SplitContainerPanel, no método que cria um novo objeto gráfico, adicionamos uma linha para criar um novo objeto dica de ferramenta:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CSplitContainerPanel::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *element=NULL; switch(type) { case GRAPH_ELEMENT_TYPE_ELEMENT : element=new CGCnvElement(type,this.GetMain(),this.GetObject(),this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break; case GRAPH_ELEMENT_TYPE_FORM : element=new CForm(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; //---... //---... case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP : element=new CHintMoveUp(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN : element=new CHintMoveDown(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_TOOLTIP : element=new CToolTip(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default : break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); return element; } //+------------------------------------------------------------------+
Agora, em cada objeto contêiner, podemos criar um novo objeto dica de ferramenta e atribuí-lo a qualquer um dos elementos gráficos existentes.
Para processar objetos dica de ferramenta, precisamos fazer alterações no arquivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh da classe coleção de elementos gráficos.
Na seção privada da classe, declararemos um método para processar objetos dica:
//--- Post-processing of the former active form under the cursor void FormPostProcessing(CForm *form,const int id, const long &lparam, const double &dparam, const string &sparam); //--- Handle Tooltip elements void TooltipProcessing(CForm *form,CForm *tooltip,CForm *tooltip_prev); //--- Add the element to the collection list bool AddCanvElmToCollection(CGCnvElement *element);
No método que procura por objetos de interação, vamos adicionar uma omissão do objeto dica de ferramenta:
//+------------------------------------------------------------------+ //| Search for interaction objects | //+------------------------------------------------------------------+ CForm *CGraphElementsCollection::SearchInteractObj(CForm *form,const int id,const long &lparam,const double &dparam,const string &sparam) { //--- If a non-empty pointer is passed if(form!=NULL) { //--- Create the list of interaction objects int total=form.CreateListInteractObj(); //--- In the loop by the created list for(int i=total-1;i>WRONG_VALUE;i--) { //--- get the next form object CForm *obj=form.GetInteractForm(i); //--- If the object is received, but is not visible, or not active, or should not be displayed, skip it if(obj==NULL || !obj.IsVisible() || !obj.Enabled() || !obj.Displayed()) continue; //--- If this is Tooltip, skip such an object for now if(obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP) { continue; } //--- If the form object is TabControl, return the selected tab under the cursor if(obj.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL) { //---... //---...
Quando procuramos um elemento gráfico sob o cursor e encontramos um objeto de dica de ferramenta, para não processá-lo, precisamos ignorá-lo aqui. Quando são feitos objetos dicas ativos que permitem a interação com o mouse, em vez de omissão, será verificado que o objeto possa interagir com o mouse. Enquanto isso, ignoramos esses objetos aqui.
Método que trata elementos ToolTip:
//+------------------------------------------------------------------+ //| Handle ToolTip elements | //+------------------------------------------------------------------+ void CGraphElementsCollection::TooltipProcessing(CForm *form,CForm *tooltip,CForm *tooltip_prev) { //--- Get the chart width and height int w=(int)::ChartGetInteger(tooltip.ChartID(),CHART_WIDTH_IN_PIXELS,tooltip.SubWindow()); int h=(int)::ChartGetInteger(tooltip.ChartID(),CHART_HEIGHT_IN_PIXELS,tooltip.SubWindow()); //--- Get cursor coordinates int x=this.m_mouse.CoordX(); int y=this.m_mouse.CoordY(); //--- If at the current X coordinate (cursor) the tooltip goes beyond the right edge of the chart, adjust the X coordinate if(x+tooltip.Width()>w) x=w-tooltip.Width(); //--- If at the current Y coordinate (cursor) the tooltip goes beyond the bottom edge of the chart, adjust the Y coordinate if(y+tooltip.Height()>h) y=h-tooltip.Height(); //--- If the tooltip object is hidden if(!tooltip.IsVisible()) { //--- If the tooltip object is shifted to the received cursor coordinates if(tooltip.Move(x,y)) { //--- Get the object the tooltip object is assigned to CForm *base=tooltip.GetBase(); //--- If the object is received, set new relative tooltip coordinates if(base!=NULL) { tooltip.SetCoordXRelative(tooltip.CoordX()-base.CoordX()); tooltip.SetCoordYRelative(tooltip.CoordY()-base.CoordY()); } } } //--- Set the tooltip drawing flag, as well as full opacity, and display the object tooltip.SetDisplayed(true); tooltip.SetOpacity(255); tooltip.Show(); //--- If the previous tooltip object exists and its name is not equal to the name of the current object (it is the same object) if(tooltip_prev!=NULL && tooltip_prev.Name()!=tooltip.Name()) { //--- Set the flag disabling the display, make the object completely transparent and hide it tooltip_prev.SetDisplayed(false); tooltip.SetOpacity(0); tooltip_prev.Hide(); } } //+------------------------------------------------------------------+
A lógica do método é descrita nos comentários ao código. Quando colocamos o objeto sob o cursor, verificamos se existe um objeto dica de ferramenta definido para ele. Se existir, chamamos esse método para processar o objeto dica. Ao método passamos o próprio objeto no qual a dica foi encontrada, um ponteiro para a dica desse objeto e um ponteiro para o último objeto dica processado.
No manipulador de eventos OnChartEvent() da classe, na seção onde a situação é manipulada quando o sinalizador de movimento de objeto é liberado (o cursor está sobre o objeto, mas nenhum dos botões foi pressionado ainda), escrevemos o seguinte trecho de código:
//---... //---... //--- If the move flag is disabled else { //--- Get the ToolTip object assigned to the form and declare the previous ToolTip CForm *tooltip=form.GetTooltip(); static CForm *tooltip_prev=NULL; //--- If the ToolTip object is received if(tooltip!=NULL) { //--- and if its type is indeed ToolTip (to avoid incorrect casting, which we sometimes get if we do not check the type) if(tooltip.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP) { //--- Get the base object set in ToolTip CForm *base=tooltip.GetBase(); //--- If the base object is received if(base!=NULL) { //--- and if the name of the base object is the same as the name of the current form (the ToolTip is bound to the form) if(base.Name()==form.Name()) { //--- Handle the received and previous ToolTip and write the current ToolTip to the variable as the previous one this.TooltipProcessing(form,tooltip,tooltip_prev); tooltip_prev=tooltip; } } } } //--- If there is no ToolTip attached to the object else { //--- If the previous ToolTip exists if(tooltip_prev!=NULL) { //--- and if its type is indeed ToolTip (to avoid incorrect casting, which we sometimes get if we do not check the type) if(tooltip_prev.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WF_TOOLTIP) { //--- Set the object non-display flag, make it completely transparent and hide it tooltip_prev.SetDisplayed(false); tooltip_prev.SetOpacity(0,false); tooltip_prev.Hide(); } } } //--- The undefined mouse status in mouse_state means releasing the left button //--- Assign the new mouse status to the variable if(mouse_state==MOUSE_FORM_STATE_NONE) mouse_state=MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED; //--- Handle moving the cursor mouse away from the graphical element this.FormPostProcessing(form,id,lparam,dparam,sparam); } } } //--- If the cursor is not above the form if(form==NULL) { //---... //---...
A lógica deste bloco de código é descrita nos comentários. Depois de atualizar o terminal, parece que as condições lógicas não estão sempre funcionando corretamente. Isso acontece quando há uma verificação simultânea do ponteiro para não ser NULL e de alguma propriedade do objeto sendo verificado. Portanto, aqui as condições que podem ser escritas em uma linha são divididas em blocos entre colchetes { }. Posso estar errado, mas em qualquer caso, este bloco de código será retrabalhado ao desenvolver a classe de objeto dica de ferramenta. Nesse caso, o código permite apenas encontrar o objeto para o qual a dica de ferramenta está definida e exibir a mesma, enquanto oculta a anterior. Isso significa que não há atrasos na exibição da dica de ferramenta e também não há uma transição suave de sua aparência. Faremos tudo isso em artigos posteriores.
Isso é tudo que precisávamos fazer hoje. Vamos testar o que temos.
Teste
Para testar, vamos pegar o Expert Advisor do artigo anterior e salvá-lo na nova pasta \MQL5\Experts\TestDoEasy\Part125\ com o novo nome TestDoEasy125.mq5.
Vamos adicionar duas novas variáveis às configurações do EA para especificar o tipo de ícone usado na dica de ferramenta e para especificar o texto do título da dica de ferramenta:
//--- input parameters sinput bool InpMovable = true; // Panel Movable flag sinput ENUM_INPUT_YES_NO InpAutoSize = INPUT_YES; // Panel Autosize sinput ENUM_AUTO_SIZE_MODE InpAutoSizeMode = AUTO_SIZE_MODE_GROW; // Panel Autosize mode sinput ENUM_BORDER_STYLE InpFrameStyle = BORDER_STYLE_SIMPLE; // Label border style sinput ENUM_ANCHOR_POINT InpTextAlign = ANCHOR_CENTER; // Label text align sinput ENUM_INPUT_YES_NO InpTextAutoSize = INPUT_NO; // Label autosize sinput ENUM_ANCHOR_POINT InpCheckAlign = ANCHOR_LEFT; // Check flag align sinput ENUM_ANCHOR_POINT InpCheckTextAlign = ANCHOR_LEFT; // Check label text align sinput ENUM_CHEK_STATE InpCheckState = CHEK_STATE_UNCHECKED; // Check flag state sinput ENUM_INPUT_YES_NO InpCheckAutoSize = INPUT_YES; // CheckBox autosize sinput ENUM_BORDER_STYLE InpCheckFrameStyle = BORDER_STYLE_NONE; // CheckBox border style sinput ENUM_ANCHOR_POINT InpButtonTextAlign = ANCHOR_CENTER; // Button text align sinput ENUM_INPUT_YES_NO InpButtonAutoSize = INPUT_YES; // Button autosize sinput ENUM_AUTO_SIZE_MODE InpButtonAutoSizeMode= AUTO_SIZE_MODE_GROW; // Button Autosize mode sinput ENUM_BORDER_STYLE InpButtonFrameStyle = BORDER_STYLE_NONE; // Button border style sinput bool InpButtonToggle = true ; // Button toggle flag sinput bool InpButtListMSelect = false; // ButtonListBox Button MultiSelect flag sinput bool InpListBoxMColumn = true; // ListBox MultiColumn flag sinput bool InpTabCtrlMultiline = false; // Tab Control Multiline flag sinput ENUM_ELEMENT_ALIGNMENT InpHeaderAlignment = ELEMENT_ALIGNMENT_TOP; // TabHeader Alignment sinput ENUM_ELEMENT_TAB_SIZE_MODE InpTabPageSizeMode = ELEMENT_TAB_SIZE_MODE_FILL; // TabHeader Size Mode sinput int InpTabControlX = 10; // TabControl X coord sinput int InpTabControlY = 20; // TabControl Y coord sinput ENUM_CANV_ELEMENT_TOOLTIP_ICON InpTooltipIcon = CANV_ELEMENT_TOOLTIP_ICON_NONE; // Tooltip Icon sinput string InpTooltipTitle = ""; // Tooltip Title //--- global variables
Depois que o controle TabControl é criado, criamos dois objetos dica de ferramenta e os atribuímos aos dois botões para rolar a fileira de títulos da esquerda para a direita:
//---... //---... //--- Create TabControl pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,InpTabControlX,InpTabControlY,pnl.Width()-30,pnl.Height()-40,clrNONE,255,true,false); CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0); if(tc!=NULL) { tc.SetTabSizeMode((ENUM_CANV_ELEMENT_TAB_SIZE_MODE)InpTabPageSizeMode); tc.SetAlignment((ENUM_CANV_ELEMENT_ALIGNMENT)InpHeaderAlignment); tc.SetMultiline(InpTabCtrlMultiline); tc.SetHeaderPadding(6,0); tc.CreateTabPages(15,0,56,20,TextByLanguage("Вкладка","TabPage")); //--- Create Tooltip for the Left button tc.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,0,0,10,10,clrNONE,0,false,false); CToolTip *tooltip=tc.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,0); if(tooltip!=NULL) { tooltip.SetDescription("Left Button Tooltip"); tooltip.SetIcon(InpTooltipIcon); tooltip.SetTitle(InpTooltipTitle); tooltip.SetTooltipText(TextByLanguage("Нажмите для прокрутки заголовков вправо","Click to scroll headings to the right")); tc.AddTooltipToArrowLeftButton(tooltip); } //--- Create Tooltip for the Right button tc.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,0,0,10,10,clrNONE,0,false,false); tooltip=tc.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TOOLTIP,1); if(tooltip!=NULL) { tooltip.SetDescription("Right Button Tooltip"); tooltip.SetIcon(ENUM_CANV_ELEMENT_TOOLTIP_ICON(InpTooltipIcon+1)); tooltip.SetTitle(InpTooltipTitle); tooltip.SetTooltipText(TextByLanguage("Нажмите для прокрутки заголовков влево","Click to scroll headings to the left")); tc.AddTooltipToArrowRightButton(tooltip); } //--- Create a text label with a tab description on each tab for(int j=0;j<tc.TabPages();j++) { //---... //---...
Como podemos ver, depois de criar um objeto ToolTip, atribuímos a ele uma descrição, um ícone, textos de título e de dicas de ferramentas, e o adicionamos ao botão correspondente. A fim de visualizar dois ícones ao mesmo tempo, para o segundo objeto ToolTip, definimos o valor do ícone como sendo um a mais do que o especificado nas configurações.
Compilamos o Expert Advisor e o iniciamos no gráfico:
Quando movemos o ponteiro do mouse sobre os botões com setas, as dicas de ferramentas aparecem imediatamente. Cada uma delas tem seu próprio ícone. As dicas não se estendem além da tela e suas coordenadas são corrigidas corretamente. Agora, nos botões com setas, vemos novas setas desenhadas como primitivas gráficas padrão. Naturalmente, o comportamento dos objetos dica de ferramenta não pode ser considerado correto. Mas hoje não conseguimos isso. O teste era apenas para ver os objetos em si e sua aparência.
O que virá a seguir?
No próximo artigo, continuaremos trabalhando no objeto ToolTip.
Todos os arquivos da versão atual da biblioteca, os arquivos do EA de teste e o indicador do controle de eventos dos gráficos para MQL5 estão anexados abaixo.
*Artigos desta série:
DoEasy. Controles (Parte 20): Objeto WinForms SplitContainer
DoEasy. Controles (Parte 21): Controle SplitContainer. Separador de painéis
DoEasy. Controles (Parte 22): SplitContainer. Alterando as propriedades do objeto criado
DoEasy. Controles (Parte 23): Apurando os objetos WinForms TabControl e SplitContainer
DoEasy. Controles (Parte 24): Objeto WinForms dica
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/11700





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso