English Русский 中文 Español Deutsch 日本語
preview
DoEasy. Controles (Parte 25): Objeto WinForms Tooltip

DoEasy. Controles (Parte 25): Objeto WinForms Tooltip

MetaTrader 5Exemplos | 13 fevereiro 2023, 15:50
111 0
Artyom Trishkin
Artyom Trishkin

Conteúdo


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.

Voltar ao conteúdo

*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

Arquivos anexados |
MQL5.zip (4557.73 KB)
Funcionalidades do assistente MQL5 que você precisa conhecer (Parte 04): Análise discriminante linear Funcionalidades do assistente MQL5 que você precisa conhecer (Parte 04): Análise discriminante linear
O trader moderno está quase sempre à procura de novas ideias. Para isso, tenta novas estratégias, modifica e descarta aquelas que não funcionam. Nesta série de artigos, tentarei provar que o assistente MQL5 é a verdadeira espinha dorsal de um trader moderno.
Aprendendo a construindo um Expert Advisor que opera de forma automática (Parte 14): Automação (VI) Aprendendo a construindo um Expert Advisor que opera de forma automática (Parte 14): Automação (VI)
Aqui vamos realmente colocar todo o conhecimento desta sequencia em prática. Vamos finalmente construir um sistema 100% automático e funcional. Mas para fazer isto, você terá que aprender uma última coisa.
Redes neurais de maneira fácil (Parte 32): Aprendizado Q distribuído Redes neurais de maneira fácil (Parte 32): Aprendizado Q distribuído
Em um dos artigos desta série, já nos iniciamos no método aprendizado Q, que calcula a média da recompensa para cada ação. Em 2017, foram apresentados 2 trabalhos simultâneos, que tiveram sucesso quanto ao estudo da função de distribuição de recompensas. Vamos considerar a possibilidade de usar essa tecnologia para resolver nossos problemas.
Ciência de dados e Aprendizado de Máquina (parte 09): O algoritmo K-vizinhos mais próximos (KNN) Ciência de dados e Aprendizado de Máquina (parte 09): O algoritmo K-vizinhos mais próximos (KNN)
Este é um algoritmo preguiçoso que não aprende com o conjunto de dados de treinamento, ele armazena o conjunto de dados e age imediatamente quando ele recebe uma nova amostra. Por mais simples que ele seja, ele é usado em uma variedade de aplicações do mundo real