English Русский 中文 Español Deutsch 日本語
Gráficos na biblioteca DoEasy (Parte 76): objeto forma e temas de cores predefinidos

Gráficos na biblioteca DoEasy (Parte 76): objeto forma e temas de cores predefinidos

MetaTrader 5Exemplos | 19 agosto 2021, 09:51
627 0
Artyom Trishkin
Artyom Trishkin

Sumário


Ideia

No último artigo, desenvolvemos a classe do objeto-elemento gráfico, que é a base para a criação de objetos gráficos mais complexos da biblioteca, e também criamos métodos para desenhar primitivas gráficas e textos. Hoje, com base neste objeto-elemento gráfico, criaremos a classe do seu objeto descendente, que será o objeto-forma. O objeto-forma já pode ser uma unidade absolutamente independente para o desenho e apresentação de controles e visualização em programas criados com base nesta biblioteca.

Mas, antes de fazer isso, falaremos sobre a GUI e seus métodos de design, e criaremos um conjunto inicial de temas de cores e tipos de objetos gráficos.
Muitos programas que usam representação gráfica de dados e fornecem interação com o mundo externo por meio de seu mecanismo gráfico permitem alterar rapidamente a aparência e o design dos objetos gráficos. Para mudar rapidamente tal aparência e o esquema de cores, usaremos um conjunto de temas. Os parâmetros dos temas criados estarão contidos num arquivo de biblioteca separado em que o usuário do programa ou programador pode alterar rapidamente as configurações de aparência e cor dos objetos gráficos.

Hoje começaremos a criar dois temas, nos quais inseriremos gradativamente mais e mais parâmetros e valores conforme desenvolvemos novos objetos e funcionalidades de biblioteca.
Para criar nossos próprios objetos gráficos, não usaremos necessariamente os temas gerados na biblioteca, embora eles possam servir como exemplo de como exatamente fazer um objeto para ser usado posteriormente.


Aprimorando as classes da biblioteca

Primeiro, no arquivo \MQL5\Include\DoEasy\Data.mqh adicionamos os índices das novas mensagens:

   MSG_LIB_SYS_FAILED_ADD_SYM_OBJ,                    // Failed to add symbol
   MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ,                 // Failed to create the graphical element object
   MSG_LIB_SYS_OBJ_ALREADY_IN_LIST,                   // Such an object is already present in the list
   MSG_LIB_SYS_FAILED_GET_DATA_GRAPH_RES,             // Failed to receive graphical resource data

...

   MSG_LIB_SYS_FAILED_ADD_BUFFER,                     // Failed to add buffer object to the list
   MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ,              // Failed to create \"Indicator buffer\" object
   MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST,                // Could not add object to the list

e os textos que correspondem aos índices recém-adicionados:

   {"Не удалось добавить символ ","Failed to add "},
   {"Не удалось создать объект-графический элемент ","Failed to create graphic element object "},
   {"Такой объект уже есть в списке","Such an object is already in the list"},
   {"Не удалось получить данные графического ресурса","Failed to get graphic resource data"},

...

   {"Не удалось добавить объект-буфер в список","Failed to add buffer object to list"},
   {"Не удалось создать объект \"Индикаторный буфер\"","Failed to create object \"Indicator buffer\""},
   {"Не удалось добавить объект в список","Failed to add object to the list"},

Olhando um pouco para o futuro: hoje, ao gerar o objeto-forma, prepararemos uma complemento para posterior criação das sombras que a forma projeta sobre os objetos que estão embaixo dela. Aqui, ao desenhar a forma, precisamos fazer ao redor dela um espaço pequeno onde será desenhada a sombra. Para especificar o tamanho desse espaço, precisamos de uma substituição de macro que indicará o tamanho de um lado em pixels. Se especificarmos 5 pixels, haverá espaço livre em torno da parte superior, inferior, esquerda e direita - cinco pixels de cada lado.

E mais uma coisa: após analisar o uso do objeto-elemento gráfico, vimos que não precisamos de algumas de suas propriedades na lista de propriedades, uma vez que elas não serão usadas para pesquisar e classificar objetos. Por esse motivo, elas precisam ser removidas da enumeração de propriedades inteiras do objeto-elemento, porque estarão contidas em variáveis protegidas/membros de classe.

Abrimos o arquivo \MQL5\Include\DoEasy\Defines.mqh e realizamos as modificações mencionadas:

Na lista de parâmetros da tela adicionamos o recuo de um lado para as sombras:

//--- Parameters of the DOM snapshot series
#define MBOOKSERIES_DEFAULT_DAYS_COUNT (1)                        // The default required number of days for DOM snapshots in the series
#define MBOOKSERIES_MAX_DATA_TOTAL     (200000)                   // Maximum number of stored DOM snapshots of a single symbol
//--- Canvas parameters
#define PAUSE_FOR_CANV_UPDATE          (16)                       // Canvas update frequency
#define NULL_COLOR                     (0x00FFFFFF)               // Zero for the canvas with the alpha channel
#define OUTER_AREA_SIZE                (5)                        // Size of one side of the outer area around the workspace
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+

Removemos duas propriedades desnecessárias da lista de propriedades inteiras do elemento gráfico:

   CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,                // Active area offset from the bottom edge of the element
   CANV_ELEMENT_PROP_OPACITY,                         // Element opacity
   CANV_ELEMENT_PROP_COLOR_BG,                        // Element background color
   CANV_ELEMENT_PROP_MOVABLE,                         // Element moveability flag

Agora, a lista de propriedades inteiras ficará assim:

//+------------------------------------------------------------------+
//| 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_NUM,                             // Element index in the list
   CANV_ELEMENT_PROP_CHART_ID,                        // Chart ID
   CANV_ELEMENT_PROP_WND_NUM,                         // Chart subwindow index
   CANV_ELEMENT_PROP_COORD_X,                         // Form's X coordinate on the chart
   CANV_ELEMENT_PROP_COORD_Y,                         // Form's Y coordinate on the chart
   CANV_ELEMENT_PROP_WIDTH,                           // Element width
   CANV_ELEMENT_PROP_HEIGHT,                          // Element height
   CANV_ELEMENT_PROP_RIGHT,                           // Element right border
   CANV_ELEMENT_PROP_BOTTOM,                          // Element bottom border
   CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,                  // Active area offset from the left edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_TOP,                   // Active area offset from the upper edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,                 // Active area offset from the right edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,                // Active area offset from the bottom edge of the element
   CANV_ELEMENT_PROP_MOVABLE,                         // Element moveability flag
   CANV_ELEMENT_PROP_ACTIVE,                          // Element activity flag
   CANV_ELEMENT_PROP_COORD_ACT_X,                     // X coordinate of the element active area
   CANV_ELEMENT_PROP_COORD_ACT_Y,                     // Y coordinate of the element active area
   CANV_ELEMENT_PROP_ACT_RIGHT,                       // Right border of the element active area
   CANV_ELEMENT_PROP_ACT_BOTTOM,                      // Bottom border of the element active area
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (21)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting
//+------------------------------------------------------------------+

Reduzimos o número total de propriedades inteiras em 2, em vez de 23 escrevemos 21.

Assim, da lista de possíveis critérios para ordenar os elementos gráficos na tela, também removeremos duas constantes, agora desnecessárias:

   SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM,             // Sort by the active area offset from the bottom edge of the element
   SORT_BY_CANV_ELEMENT_OPACITY,                      // Sort by the element opacity
   SORT_BY_CANV_ELEMENT_COLOR_BG,                     // Sort by the element background color
   SORT_BY_CANV_ELEMENT_MOVABLE,                      // Sort by the element moveability flag

A lista completa agora ficará assim:

//+------------------------------------------------------------------+
//| 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_NUM,                          // Sort by form index in the list
   SORT_BY_CANV_ELEMENT_CHART_ID,                     // Sort by chart ID
   SORT_BY_CANV_ELEMENT_WND_NUM,                      // Sort by chart window index
   SORT_BY_CANV_ELEMENT_COORD_X,                      // Sort by the element X coordinate on the chart
   SORT_BY_CANV_ELEMENT_COORD_Y,                      // Sort by the element Y coordinate on the chart
   SORT_BY_CANV_ELEMENT_WIDTH,                        // Sort by the element width
   SORT_BY_CANV_ELEMENT_HEIGHT,                       // Sort by the element height
   SORT_BY_CANV_ELEMENT_RIGHT,                        // Sort by the element right border
   SORT_BY_CANV_ELEMENT_BOTTOM,                       // Sort by the element bottom border
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT,               // Sort by the active area offset from the left edge of the element
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_TOP,                // Sort by the active area offset from the top edge of the element
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_RIGHT,              // Sort by the active area offset from the right edge of the element
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM,             // Sort by the active area offset from the bottom edge of the element
   SORT_BY_CANV_ELEMENT_MOVABLE,                      // Sort by the element moveability flag
   SORT_BY_CANV_ELEMENT_ACTIVE,                       // Sort by the element activity flag
   SORT_BY_CANV_ELEMENT_COORD_ACT_X,                  // Sort by X coordinate of the element active area
   SORT_BY_CANV_ELEMENT_COORD_ACT_Y,                  // Sort by Y coordinate of the element active area
   SORT_BY_CANV_ELEMENT_ACT_RIGHT,                    // Sort by the right border of the element active area
   SORT_BY_CANV_ELEMENT_ACT_BOTTOM,                   // Sort by the bottom border of the element active area
//--- 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
  };
//+------------------------------------------------------------------+

Todos os nossos objetos gráficos são criados a partir do objeto-elemento gráfico. Ele, por sua vez, é o herdeiro do objeto base de todos objetos gráficos da biblioteca (objeto esse que por sua vez é herdado da classe base da biblioteca padrão CObject) Todas as propriedades de cada classe pai são transferidas "por herança" para seus descendentes. Portanto, se precisarmos de propriedades comuns a todos os objetos gráficos, elas precisam estar localizadas nos objetos base de toda a árvore de herança. No nosso caso, o objeto da classe CGBaseObj atenderá esse requisito para objetos gráficos da biblioteca.

Precisamos controlar a visibilidade dos objetos gráficos. Para fazer isso, em vez de excluir, ocultar ou remover o objeto gráfico, só precisamos especificar - na propriedadeOBJPROP_TIMEFRAMES do objeto gráfico - os sinalizadores necessários, e, assim, o objeto será ocultado ou mostrado no gráfico. Além disso, é mostrado acima de todos os outros. Assim, poderemos não só controlar a visibilidade do objeto no gráfico, mas também colocar o objeto desejado acima de todos os outros.

De todo o conjunto de sinalizadores do objeto precisamos dos sinalizadores: OBJ_NO_PERIODS para ocultar o objeto e OBJ_ALL_PERIODS para mostrá-lo no gráfico. Para mover um objeto para a frente, simplesmente ocultamos o objeto e, em seguida, o mostramos. Assim, o objeto se moverá para o primeiro plano.

Ao arquivo do objeto base \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh adicionamos novas propriedades e métodos.

Na seção protegida da classe declaramos uma variável para armazenar a propriedade de visibilidade do objeto:

//+------------------------------------------------------------------+
//| Class of the base object of the library graphical objects        |
//+------------------------------------------------------------------+
class CGBaseObj : public CObject
  {
private:

protected:
   string            m_name_prefix;                      // Object name prefix
   string            m_name;                             // Object name
   long              m_chart_id;                         // Chart ID
   int               m_subwindow;                        // Subwindow index
   int               m_shift_y;                          // Subwindow Y coordinate shift
   int               m_type;                             // Object type
   bool              m_visible;                          // Object visibility
   
//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void)                      { return true; }
   virtual void      StructToObject(void){;}

public:

Na seção pública da classe, escrevemos o método para definir o sinalizador de visibilidade do objeto e estabelecer a propriedade no objeto, bem como o método para retornar o valor de visibilidade de um objeto no gráfico:

public:
//--- Return the values of class variables
   string            Name(void)                          const { return this.m_name;      }
   long              ChartID(void)                       const { return this.m_chart_id;  }
   int               SubWindow(void)                     const { return this.m_subwindow; }
//--- (1) Set and (2) return the object visibility
   void              SetVisible(const bool flag)   
                       { 
                        long value=(flag ? OBJ_ALL_PERIODS : 0);
                        if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value))
                           this.m_visible=flag;
                       }
   bool              IsVisible(void)                     const { return this.m_visible;   }

//--- The virtual method returning the object type
   virtual int       Type(void)                          const { return this.m_type;      }

O método que define a visibilidade do objeto primeiro verifica o valor do sinalizador e, dependendo do valor passado (true ou false) envia uma solicitação para definir o valor do objeto - OBJ_ALL_PERIODS para mostrar o objeto no gráfico ou 0 para ocultá-lo. Se a solicitação for colocada com sucesso na fila de eventos do gráfico, na variável m_visible é escrito o valor do sinalizador passado ao método, sinalizador esse que pode ser encontrado usando o método IsVisible(), que retorna o valor desta variável.

Na lista de inicialização do construtor da classe, iniciamos uma nova variável com o valor false:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CGBaseObj::CGBaseObj() : m_shift_y(0), m_type(0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_")
  {
  }
//+------------------------------------------------------------------+


Modificamos a classe do objeto-elemento gráfico no arquivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh.

Na seção protegida da classe, declaramos uma variável sinalizador que indicará a presença/ausência da sombra que o objeto projeta e outra variável para armazenar a cor de fundo do gráfico - precisaremos disso no futuro ao desenhar sombras:

//+------------------------------------------------------------------+
//| Class of the base object of the library graphical objects        |
//+------------------------------------------------------------------+
class CGCnvElement : public CGBaseObj
  {
protected:
   CCanvas           m_canvas;                                 // CCanvas class object
   CPause            m_pause;                                  // Pause class object
   bool              m_shadow;                                 // Shadow presence
   color             m_chart_color_bg;                         // Chart background color
//--- Return the cursor position relative to the (1) entire element and (2) the element's active area
   bool              CursorInsideElement(const int x,const int y);
   bool              CursorInsideActiveArea(const int x,const int y);
//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);

private:

Como removemos duas constantes da enumeração das propriedades inteiras do objeto, agora devemos armazená-las nas variáveis de classe.
Vamos declará-las na seção privada
:

   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];    // Integer properties
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];   // Real properties
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];   // String properties
   
   ENUM_TEXT_ANCHOR  m_text_anchor;                            // Current text alignment
   color             m_color_bg;                               // Element background color
   uchar             m_opacity;                                // Element opacity
   
//--- Return the index of the array the order's (1) double and (2) string properties are located at

Agora, as propriedades "cor de fundo do elemento" e "opacidade do elemento" serão gravadas nessas variáveis.

Para gerar a aparência de objetos gráficos, precisamos de um método que nos permita alterar a luminosidade de cor.
Tal método é um dos componentes do modelo de cores HSL:

HSL, HLS ou HSI (hue, saturation, lightness (intensity)) — modelo de cor no qual as coordenadas de cor são matiz, saturação e luminosidade. Vale a pena notar que HSV e HSL são dois modelos de cores diferentes (lightness é a luminosidade, o que é diferente de brilho).

Ao desenhar primitivas gráficas, precisamos clarear as partes iluminadas da imagem e escurecer as partes sombreadas convencionalmente. Ao mesmo tempo, não devemos afetar a cor da imagem em si. Para isso, aplicaremos um método que converte o modelo de cores ARGB em HSL e altera o brilho dos pixels da parte desejada da imagem.

Na seção privada da classe, declaramos este método:

//--- Update the coordinates (shift the canvas)
   bool              Move(const int x,const int y,const bool redraw=false);

//--- Change the color lightness by the specified amount
   uint              ChangeColorLightness(const uint clr,const double change_value);

protected:

Visto que o objeto-elemento gráfico será o objeto principal para a criação de outros mais complexos que serão seus herdeiros, então, tendo em mente o conceito de construção de objetos de biblioteca, onde a classe pai possui um construtor protegido, que especifica os parâmetros do objeto herdado criado, precisamos criar um construtor paramétrico protegido para o objeto-elemento também. Ele receberá parâmetros que especificarão o tipo do objeto-herdeiro que será criado a partir do elemento gráfico (hoje será um objeto-forma).

Na seção protegida da classe declaramos um novo construtor paramétrico protegido:

protected:
//--- Protected constructor
                     CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                  const long    chart_id,
                                  const int     wnd_num,
                                  const string  name,
                                  const int     x,
                                  const int     y,
                                  const int     w,
                                  const int     h);
public:

Vamos passar para ele apenas os parâmetros básicos para a criação do objeto. Todos os outros parâmetros serão configurados para o objeto após sua criação com sucesso. Isso será feito na classe-coleção de objetos gráficos da biblioteca, o que ainda não começamos a fazer, mas começaremos no futuro.

Na lista de inicialização do construtor padrão (não paramétrico) escrevemos a inicialização do sinalizador de presença de sombra e o gráfico de cores de fundo:

public:
//--- Parametric constructor
                     CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                  const int     element_id,
                                  const int     element_num,
                                  const long    chart_id,
                                  const int     wnd_num,
                                  const string  name,
                                  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);
//--- Default constructor/Destructor
                     CGCnvElement() : m_shadow(false),m_chart_color_bg((color)::ChartGetInteger(::ChartID(),CHART_COLOR_BACKGROUND)) {;}


No bloco de métodos para facilitar o acesso aos parâmetros do objeto vamos escrever novos métodos para definir propriedades do objeto:

//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- Set the (1) X, (2) Y coordinates, (3) element width, (4) height, (5) right (6) and bottom edge,
   bool              SetCoordX(const int coord_x);
   bool              SetCoordY(const int coord_y);
   bool              SetWidth(const int width);
   bool              SetHeight(const int height);
   void              SetRightEdge(void)                        { this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());           }
   void              SetBottomEdge(void)                       { this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());         }
//--- 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) the element background color and (7) the element 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);
   void              SetColorBackground(const color colour)    { this.m_color_bg=colour;                                               }
   void              SetOpacity(const uchar value,const bool redraw=false);

//--- Set the flag of (1) object moveability, (2) activity, (3) element ID, (4) element index in the list and (5) shadow presence
   void              SetMovable(const bool flag)               { this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,flag);                     }
   void              SetActive(const bool flag)                { this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,flag);                      }
   void              SetID(const int id)                       { this.SetProperty(CANV_ELEMENT_PROP_ID,id);                            }
   void              SetNumber(const int number)               { this.SetProperty(CANV_ELEMENT_PROP_NUM,number);                       }
   void              SetShadow(const bool flag);
   
//--- Return the shift (1) of the left, (2) right, (3) top and (4) bottom edge of the element active area

Os métodos para retornar a cor de fundo e a opacidade agora retornam os valores gravados nas variáveis recém-declaradas:

//--- Return (1) the background color, (2) the opacity, coordinate (3) of the right and (4) bottom element edge
   color             ColorBackground(void)               const { return this.m_color_bg;                                               }
   uchar             Opacity(void)                       const { return this.m_opacity;                                                }
   int               RightEdge(void)                     const { return this.CoordX()+this.m_canvas.Width();                           }
   int               BottomEdge(void)                    const { return this.CoordY()+this.m_canvas.Height();                          }
//--- Return the (1) X, (2) Y coordinates, (3) element width and (4) height,

No final da lista adicionamos um método para retornar o sinalizador de desenho da sombra projetada pelo objeto, o método que retorna a cor de fundo do gráfico,
e o método que move o objeto para a frente (acima de todos os outros objetos gráficos no gráfico):

//--- Return (1) the element ID, (2) element index in the list, (3) flag of the form shadow presence and (4) the chart background color
   int               ID(void)                            const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ID);                   }
   int               Number(void)                        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_NUM);                  }
   bool              IsShadow(void)                      const { return this.m_shadow;                                                 }
   color             ChartColorBackground(void)          const { return this.m_chart_color_bg;                                         }
//--- Set the object above all
   void              BringToTop(void)                          { CGBaseObj::SetVisible(false); CGBaseObj::SetVisible(true);            }
//+------------------------------------------------------------------+

Como se pode ver, para definir o objeto acima de todos os outros, apenas o escondemos e imediatamente mostramos usando os métodos da classe pai discutidos acima.

Do construtor paramétrico removemos as linhas com a configuração das propriedades agora removidas:

      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0);                    // Active area offset from the bottom edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_OPACITY,opacity);                       // Element opacity
      this.SetProperty(CANV_ELEMENT_PROP_COLOR_BG,colour);                       // Element color
      this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable);                       // Element moveability flag

Agora vamos escrever estas novas propriedades nas novas variáveis:

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const int      element_id,
                           const int      element_num,
                           const long     chart_id,
                           const int      wnd_num,
                           const string   name,
                           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.m_chart_color_bg=(color)::ChartGetInteger(chart_id,CHART_COLOR_BACKGROUND);
   this.m_name=this.m_name_prefix+name;
   this.m_chart_id=chart_id;
   this.m_subwindow=wnd_num;
   this.m_type=element_type;
   this.SetFont("Calibri",8);
   this.m_text_anchor=0;
   this.m_color_bg=colour;
   this.m_opacity=opacity;
   if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,colour,opacity,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_WND_NUM,CGBaseObj::SubWindow());        // Chart subwindow index
      this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name());            // Element object name
      this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type);                     // Graphical element type
      this.SetProperty(CANV_ELEMENT_PROP_ID,element_id);                         // Element ID
      this.SetProperty(CANV_ELEMENT_PROP_NUM,element_num);                       // Element index in the list
      this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x);                             // Element's X coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y);                             // Element's Y coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w);                               // Element width
      this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h);                              // Element height
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0);                      // Active area offset from the left edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0);                       // Active area offset from the upper edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0);                     // Active area offset from the right edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0);                    // Active area offset from the bottom edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable);                       // Element moveability flag
      this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,activity);                       // Element activity flag
      this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());                // Element right border
      this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());              // Element bottom border
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft());     // X coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop());      // Y coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight());      // Right border of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom());    // Bottom border of the element active area
     }
   else
     {
      ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.m_name);
     }
  }
//+------------------------------------------------------------------+

O novo construtor paramétrico protegido praticamente não difere do anterior:

//+------------------------------------------------------------------+
//| Protected constructor                                            |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const long    chart_id,
                           const int     wnd_num,
                           const string  name,
                           const int     x,
                           const int     y,
                           const int     w,
                           const int     h) : m_shadow(false)
  {
   this.m_chart_color_bg=(color)::ChartGetInteger(chart_id,CHART_COLOR_BACKGROUND);
   this.m_name=this.m_name_prefix+name;
   this.m_chart_id=chart_id;
   this.m_subwindow=wnd_num;
   this.m_type=element_type;
   this.SetFont("Calibri",8);
   this.m_text_anchor=0;
   this.m_color_bg=NULL_COLOR;
   this.m_opacity=0;
   if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,this.m_color_bg,this.m_opacity,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_WND_NUM,CGBaseObj::SubWindow());        // Chart subwindow index
      this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name());            // Element object name
      this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type);                     // Graphical element type
      this.SetProperty(CANV_ELEMENT_PROP_ID,0);                                  // Element ID
      this.SetProperty(CANV_ELEMENT_PROP_NUM,0);                                 // Element index in the list
      this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x);                             // Element's X coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y);                             // Element's Y coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w);                               // Element width
      this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h);                              // Element height
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0);                      // Active area offset from the left edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0);                       // Active area offset from the upper edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0);                     // Active area offset from the right edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0);                    // Active area offset from the bottom edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,false);                         // Element moveability flag
      this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,false);                          // Element activity flag
      this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());                // Element right border
      this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());              // Element bottom border
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft());     // X coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop());      // Y coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight());      // Right border of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom());    // Bottom border of the element active area
     }
   else
     {
      ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.m_name);
     }
  }
//+------------------------------------------------------------------+

Para ele são passados menos valores, a cor de fundo do elemento é definida como branco transparente e é definida a transparência total do elemento.

Do método para criar a estrutura do objeto removemos as linhas desnecessárias agora:

   this.m_struct_obj.act_shift_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);// Active area offset from the bottom edge of the element
   this.m_struct_obj.opacity=(uchar)this.GetProperty(CANV_ELEMENT_PROP_OPACITY);                // Непрозрачность элемента
   this.m_struct_obj.color_bg=(color)this.GetProperty(CANV_ELEMENT_PROP_COLOR_BG);              // Цвет фона элемента
   this.m_struct_obj.movable=(bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE);                 // Element moveability flag

e escrevemos abaixo o armazenamento desses parâmetros a partir das novas variáveis:

//+------------------------------------------------------------------+
//| Create the object structure                                      |
//+------------------------------------------------------------------+
bool CGCnvElement::ObjectToStruct(void)
  {
//--- Save integer properties
   this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID);                            // Element ID
   this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE);                        // Graphical element type
   this.m_struct_obj.number=(int)this.GetProperty(CANV_ELEMENT_PROP_NUM);                       // Element ID in the list
   this.m_struct_obj.chart_id=this.GetProperty(CANV_ELEMENT_PROP_CHART_ID);                     // Chart ID
   this.m_struct_obj.subwindow=(int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM);                // Chart subwindow index
   this.m_struct_obj.coord_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X);                  // Form's X coordinate on the chart
   this.m_struct_obj.coord_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y);                  // Form's Y coordinate on the chart
   this.m_struct_obj.width=(int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH);                      // Element width
   this.m_struct_obj.height=(int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT);                    // Element height
   this.m_struct_obj.edge_right=(int)this.GetProperty(CANV_ELEMENT_PROP_RIGHT);                 // Element right edge
   this.m_struct_obj.edge_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_BOTTOM);               // Element bottom edge
   this.m_struct_obj.act_shift_left=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT);    // Active area offset from the left edge of the element
   this.m_struct_obj.act_shift_top=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP);      // Active area offset from the top edge of the element
   this.m_struct_obj.act_shift_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT);  // Active area offset from the right edge of the element
   this.m_struct_obj.act_shift_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);// Active area offset from the bottom edge of the element


   this.m_struct_obj.movable=(bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE);                 // Element moveability flag
   this.m_struct_obj.active=(bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE);                   // Element activity flag
   this.m_struct_obj.coord_act_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_X);          // X coordinate of the element active area
   this.m_struct_obj.coord_act_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y);          // Y coordinate of the element active area
   this.m_struct_obj.coord_act_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_RIGHT);        // Right border of the element active area
   this.m_struct_obj.coord_act_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM);      // Bottom border of the element active area
   this.m_struct_obj.color_bg=this.m_color_bg;                                                  // Element background color
   this.m_struct_obj.opacity=this.m_opacity;                                                    // Element opacity
//--- Save real properties

//--- Save string properties
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj);// Graphical element object name
   ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res);// Graphical resource name
   //--- Save the structure to the uchar array
   ::ResetLastError();
   if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array))
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY),(string)::GetLastError());
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

É isso mesmoque vamos fazer no método de criação do objeto a partir de uma estrutura:

//+------------------------------------------------------------------+
//| Create the object from the structure                                      |
//+------------------------------------------------------------------+
void CGCnvElement::StructToObject(void)
  {
//--- Save integer properties
   this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id);                                 // Element ID
   this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type);                             // Graphical element type
   this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number);                            // Element index in the list
   this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,this.m_struct_obj.chart_id);                     // Chart ID
   this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,this.m_struct_obj.subwindow);                     // Chart subwindow index
   this.SetProperty(CANV_ELEMENT_PROP_COORD_X,this.m_struct_obj.coord_x);                       // Form's X coordinate on the chart
   this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,this.m_struct_obj.coord_y);                       // Form's Y coordinate on the chart
   this.SetProperty(CANV_ELEMENT_PROP_WIDTH,this.m_struct_obj.width);                           // Element width
   this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,this.m_struct_obj.height);                         // Element height
   this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.m_struct_obj.edge_right);                      // Element right edge
   this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.m_struct_obj.edge_bottom);                    // Element bottom edge
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,this.m_struct_obj.act_shift_left);         // Active area offset from the left edge of the element
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,this.m_struct_obj.act_shift_top);           // Active area offset from the upper edge of the element
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,this.m_struct_obj.act_shift_right);       // Active area offset from the right edge of the element
   this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,this.m_struct_obj.act_shift_bottom);     // Active area offset from the bottom edge of the element
                                                                                           
                                                                                           
   this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,this.m_struct_obj.movable);                       // Element moveability flag
   this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,this.m_struct_obj.active);                         // Element activity flag
   this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.m_struct_obj.coord_act_x);               // X coordinate of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.m_struct_obj.coord_act_y);               // Y coordinate of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.m_struct_obj.coord_act_right);             // Right border of the element active area
   this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.m_struct_obj.coord_act_bottom);           // Bottom border of the element active area
   this.m_color_bg=this.m_struct_obj.color_bg;                                                  // Element background color
   this.m_opacity=this.m_struct_obj.opacity;                                                    // Element opacity
//--- Save real properties

//--- Save string properties
   this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj));// Graphical element object name
   this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res));// Graphical resource name
  }
//+------------------------------------------------------------------+

No método que cria o objeto-elemento gráfico,agora vamos apagar completamente o fundo do objeto, preenchendo com uma cor branca transparente:

//+------------------------------------------------------------------+
//| Create the graphical element object                                |
//+------------------------------------------------------------------+
bool CGCnvElement::Create(const long chart_id,     // Chart ID
                          const int wnd_num,       // Chart subwindow
                          const string name,       // Element name
                          const int x,             // X coordinate
                          const int y,             // Y coordinate
                          const int w,             // Width
                          const int h,             // Height
                          const color colour,      // Background color
                          const uchar opacity,     // Opacity
                          const bool redraw=false) // Flag indicating the need to redraw
                         
  {
   if(this.m_canvas.CreateBitmapLabel(chart_id,wnd_num,name,x,y,w,h,COLOR_FORMAT_ARGB_NORMALIZE))
     {
      this.Erase(NULL_COLOR);
      this.m_canvas.Update(redraw);
      this.m_shift_y=(int)::ChartGetInteger(chart_id,CHART_WINDOW_YDISTANCE,wnd_num);
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

No método que define a opacidade do elemento, agora em vez de gravar na propriedade removida objeto vamos inserir a opacidade na variável:

//+------------------------------------------------------------------+
//| Set the element opacity                                          |
//+------------------------------------------------------------------+
void CGCnvElement::SetOpacity(const uchar value,const bool redraw=false)
  {
   this.m_canvas.TransparentLevelSet(value);
   this.m_opacity=value;
   this.m_canvas.Update(redraw);
  }
//+------------------------------------------------------------------+

Novo método para alterar a luminosidade:

//+------------------------------------------------------------------+
//| Change the color lightness by the specified amount               |
//+------------------------------------------------------------------+
uint CGCnvElement::ChangeColorLightness(const uint clr,const double change_value)
  {
   if(change_value==0.0)
      return clr;
   double a=GETRGBA(clr);
   double r=GETRGBR(clr);
   double g=GETRGBG(clr);
   double b=GETRGBB(clr);
   double h=0,s=0,l=0;
   CColors::RGBtoHSL(r,g,b,h,s,l);
   double nl=l+change_value;
   if(nl>1.0) nl=1.0;
   if(nl<0.0) nl=0.0;
   CColors::HSLtoRGB(h,s,nl,r,g,b);
   return ARGB(a,r,g,b);
  }
//+------------------------------------------------------------------+

Aqui:
Verificamos o valor passado para o método, a fim de mudar a luminosidade, e se for passado zero, nada precisará ser alterado, retornaremos a cor inalterada. The method
Em seguida, obtemos separadamente cada um dos componentes de cor ARGB passada para o método e convertemos os componentes RGB para modelo de cores HSL.
Após a conversão, os valores de cada um dos componentes do modelo HSL serão escritos nas variáveis correspondentes (precisamos do componente l).
Adicionamos a ele o valor passado para o método (valores change_value pode, ser de -1,0 a 1,0) e o corrigimos quando ele sair dos intervalos permitidos.
Em seguida, convertemos o modelo HSL de volta em RGB e devolvemos o modelo ARGB obtido a partir dos novos componentes de cor gerados pela conversão do modelo HSL em RGB.


Temas de cores e tipos de formas

A biblioteca irá suportar a criação de diferentes objetos - elementos gráficos, formas baseadas neles, janelas, etc. Cada forma, janela ou imagem (quadros, separadores, listas suspensas, etc.) pode ter estilos de exibição completamente diferentes. Mas, num mesmo programa, seria estranho ter distintos objetos com estilos, cores e tipos de formatação diferentes.
Para tornar mais fácil a escrita de objetos cuja aparência/formato é idêntico e pertencem ao mesmo programa, apresentaremos estilos de desenho, tipos de objetos e esquemas de cores. Isso permitirá que o usuário final selecione o estilo e o tema de cores nas configurações do programa, e o programador fique descansado, já que os temas e estilos selecionados reconstruirão imediatamente todos os objetos de acordo com um critério. Bastará apenas fazer as alterações e adições necessárias ao arquivo de configurações gráficas, que listará todas as cores e parâmetros de objetos e primitivas necessários.

Já fizemos algo semelhante, quando criamos a classe de mensagens da biblioteca - existe uma lista de índices de mensagens e uma série de textos que correspondem aos índices de mensagens. Em quase todos os novos artigos, a primeira coisa que fazemos é inserir novos dados.
Assim, o arquivo de configurações gráficas será feito da mesma maneira: teremos uma enumeração de temas de cores e de estilos de objetos, e as matrizes correspondentes onde iremos inserir gradualmente novos parâmetros e valores para cada propriedade recém-adicionada ou para cada tema e cores recém-criados.

Na pasta raiz da biblioteca \MQL5\Include\DoEasy\ criamos um novo arquivo de inclusão GraphINI.mqh e inserimos nele a quantidade de temas de cores:

//+------------------------------------------------------------------+
//|                                                     GraphINI.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/pt/users/artmedia70"
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define TOTAL_COLOR_THEMES             (2)      // Number of color schemes
//+------------------------------------------------------------------+

Para obter um exemplo de como usar este arquivo de configurações, serão suficientes dois temas de cores. Posteriormente, aumentaremos seu número.

Abaixo vamos escrever os índices de temas de cor e os índices dos parâmetros de um tema - cada tema terá o mesmo número de parâmetros:

//+------------------------------------------------------------------+
//|                                                     GraphINI.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/pt/users/artmedia70"
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define TOTAL_COLOR_THEMES             (2)      // Number of color schemes
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| List of color scheme indices                                     |
//+------------------------------------------------------------------+
enum ENUM_COLOR_THEMES
  {
   COLOR_THEME_BLUE_STEEL,                      // Blue steel
   COLOR_THEME_LIGHT_CYAN_GRAY,                 // Light cyan gray
  };
//+------------------------------------------------------------------+
//| List of indices of color scheme parameters                       |
//+------------------------------------------------------------------+
enum ENUM_COLOR_THEME_COLORS
  {
   COLOR_THEME_COLOR_FORM_BG,                   // Form background color
   COLOR_THEME_COLOR_FORM_FRAME,                // Form frame color
   COLOR_THEME_COLOR_FORM_FRAME_OUTER,          // Form outer frame color
   COLOR_THEME_COLOR_FORM_SHADOW,               // Form shadow color
  };
#define TOTAL_COLOR_THEME_COLORS       (4)      // Number of parameters in the color theme
//+------------------------------------------------------------------+

Os nomes das constantes dessas enumerações facilitarão o acesso a cada tema de cor e parâmetro.

Abaixo vamos escrever uma matriz bidimensional, contendo na primeira dimensão os temas de cores e na segunda, os índices de parâmetros de cores para renderizar diferentes propriedades de objetos:

//+------------------------------------------------------------------+
//|                                                     GraphINI.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/pt/users/artmedia70"
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define TOTAL_COLOR_THEMES             (2)      // Number of color schemes
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| List of color scheme indices                                     |
//+------------------------------------------------------------------+
enum ENUM_COLOR_THEMES
  {
   COLOR_THEME_BLUE_STEEL,                      // Blue steel
   COLOR_THEME_LIGHT_CYAN_GRAY,                 // Light cyan gray
  };
//+------------------------------------------------------------------+
//| List of indices of color scheme parameters                       |
//+------------------------------------------------------------------+
enum ENUM_COLOR_THEME_COLORS
  {
   COLOR_THEME_COLOR_FORM_BG,                   // Form background color
   COLOR_THEME_COLOR_FORM_FRAME,                // Form frame color
   COLOR_THEME_COLOR_FORM_FRAME_OUTER,          // Form outer frame color
   COLOR_THEME_COLOR_FORM_SHADOW,               // Form shadow color
  };
#define TOTAL_COLOR_THEME_COLORS       (4)      // Number of parameters in the color theme
//+------------------------------------------------------------------+
//| The array containing color schemes                               |
//+------------------------------------------------------------------+
color array_color_themes[TOTAL_COLOR_THEMES][TOTAL_COLOR_THEME_COLORS]=
  {
//--- Parameters of the "Blue steel" color scheme
   {
      C'134,160,181',                           // Form background color
      C'134,160,181',                           // Form frame color
      clrDimGray,                               // Form outer frame color
      C'46,85,117',                             // Form shadow color
   },
//--- Parameters of the "Light cyan gray" color scheme
   {
      C'181,196,196',                           // Form background color
      C'181,196,196',                           // Form frame color
      clrGray,                                  // Form outer frame color
      C'130,147,153',                           // Form shadow color
   },
  };
//+------------------------------------------------------------------+

É nesta matriz que iremos inscrever gradualmente novas cores para cada parâmetro recém-adicionado do objeto gráfico cuja cor deve depender do esquema de cores selecionado.

A seguir, vamos inserir enumerações de tipos de suavização ao desenhar primitivas, estilos de bordas, tipos e estilos de formas - a mesma enumeração de índices de propriedades de estilos de formas e seus parâmetros que para os temas de cores:

//+------------------------------------------------------------------+
//| Smoothing types                                                  |
//+------------------------------------------------------------------+
enum ENUM_SMOOTHING_TYPE
  {
   SMOOTHING_TYPE_NONE,                         // No smoothing
   SMOOTHING_TYPE_AA,                           // Anti-aliasing
   SMOOTHING_TYPE_WU,                           // Wu
   SMOOTHING_TYPE_THICK,                        // Thick
   SMOOTHING_TYPE_DUAL,                         // Dual
  };
//+------------------------------------------------------------------+
//| Frame styles                                                     |
//+------------------------------------------------------------------+
enum ENUM_FRAME_STYLE
  {
   FRAME_STYLE_SIMPLE,                          // Simple frame
   FRAME_STYLE_FLAT,                            // Flat frame
   FRAME_STYLE_BEVEL,                           // Embossed (convex)
   FRAME_STYLE_STAMP,                           // Embossed (concave)
  };
//+------------------------------------------------------------------+
//| Form types                                                       |
//+------------------------------------------------------------------+
enum ENUM_FORM_TYPE
  {
   FORM_TYPE_SQUARE,                            // Rectangular
  };
//+------------------------------------------------------------------+
//| Form styles                                                      |
//+------------------------------------------------------------------+
enum ENUM_FORM_STYLE
  {
   FORM_STYLE_FLAT,                             // Flat form
   FORM_STYLE_BEVEL,                            // Embossed form
  };
#define TOTAL_FORM_STYLES
//+------------------------------------------------------------------+
//| List of form style parameter indices                             |
//+------------------------------------------------------------------+
enum ENUM_FORM_STYLE_PARAMS
  {
   FORM_STYLE_FRAME_WIDTH_LEFT,                 // Form frame width to the left
   FORM_STYLE_FRAME_WIDTH_RIGHT,                // Form frame width to the right
   FORM_STYLE_FRAME_WIDTH_TOP,                  // Form frame width on top
   FORM_STYLE_FRAME_WIDTH_BOTTOM,               // Form frame width below
   FORM_STYLE_FRAME_SHADOW_OPACITY,             // Shadow opacity
  };
#define TOTAL_FORM_STYLE_PARAMS        (5)      // Number of form style parameters
//+------------------------------------------------------------------+
//| Array containing form style parameters                           |
//+------------------------------------------------------------------+
int array_form_style[TOTAL_FORM_STYLES][TOTAL_FORM_STYLE_PARAMS]=
  {
//--- "Flat form" style parameters
   {
      3,                                        // Form frame width to the left
      3,                                        // Form frame width to the right
      3,                                        // Form frame width on top
      3,                                        // Form frame width below
      80,                                       // Shadow opacity
   },
//--- "Embossed form" style parameters
   {
      4,                                        // Form frame width to the left
      4,                                        // Form frame width to the right
      4,                                        // Form frame width on top
      4,                                        // Form frame width below
      100,                                      // Shadow opacity
   },
  };
//+------------------------------------------------------------------+

Nesta segunda matriz, cuja lógica de construção é idêntica à matriz de temas de cores, também adicionaremos gradualmente novos parâmetros para a construção de elementos, formas, janelas e outros objetos, cujos parâmetros devem depender do estilo de construção da aparência de objetos escolhido.

Para selecionar o estilo desejado de construção de objeto e tema de cores, inseriremos novas enumerações para os parâmetros de entrada dos programas no arquivo \MQL5\Include\DoEasy\InpData.mqh. No início anexamos o arquivo recém-criado GraphINI.mqh:

//+------------------------------------------------------------------+
//|                                                      InpData.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/pt/users/artmedia70"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "GraphINI.mqh"
//+------------------------------------------------------------------+

Em seguida, no bloco de código para compilar em inglês e russo vamos inserir novas enumerações de parâmetros de entrada para escolher o tema de cores:

//+------------------------------------------------------------------+
//|                                                      InpData.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/pt/users/artmedia70"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "GraphINI.mqh"
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define COMPILE_EN // Comment out the string for compilation in Russian 
//+------------------------------------------------------------------+
//| Input enumerations                                               |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| English language inputs                                          |
//+------------------------------------------------------------------+
#ifdef COMPILE_EN
//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                              // Work only with the current Symbol
   SYMBOLS_MODE_DEFINES,                              // Work with a given list of Symbols
   SYMBOLS_MODE_MARKET_WATCH,                         // Working with Symbols from the "Market Watch" window
   SYMBOLS_MODE_ALL                                   // Work with a complete list of Symbols
  };
//+------------------------------------------------------------------+
//| Mode of working with timeframes                                  |
//+------------------------------------------------------------------+
enum ENUM_TIMEFRAMES_MODE
  {
   TIMEFRAMES_MODE_CURRENT,                           // Work only with the current timeframe
   TIMEFRAMES_MODE_LIST,                              // Work with a given list of timeframes
   TIMEFRAMES_MODE_ALL                                // Work with a complete list of timeframes
  };
//+------------------------------------------------------------------+
//| Choice "Yes"/"No"                                                |
//+------------------------------------------------------------------+
enum ENUM_INPUT_YES_NO
  {
   INPUT_NO  = 0,                                     // No
   INPUT_YES = 1                                      // Yes
  };
//+------------------------------------------------------------------+
//| Select color themes                                              |
//+------------------------------------------------------------------+
enum ENUM_INPUT_COLOR_THEME
  {
   INPUT_COLOR_THEME_BLUE_STEEL,                      // Blue steel
   INPUT_COLOR_THEME_LIGHT_CYAN_GRAY,                 // Light cyan gray
  };
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Russian language inputs                                          |
//+------------------------------------------------------------------+
#else  
//+------------------------------------------------------------------+
//| Modes of working with symbols                                    |
//+------------------------------------------------------------------+
enum ENUM_SYMBOLS_MODE
  {
   SYMBOLS_MODE_CURRENT,                              // Work with the current symbol only
   SYMBOLS_MODE_DEFINES,                              // Work with the specified symbol list
   SYMBOLS_MODE_MARKET_WATCH,                         // Work with the Market Watch window symbols
   SYMBOLS_MODE_ALL                                   // Work with the full symbol list
  };
//+------------------------------------------------------------------+
//| Mode of working with timeframes                                  |
//+------------------------------------------------------------------+
enum ENUM_TIMEFRAMES_MODE
  {
   TIMEFRAMES_MODE_CURRENT,                           // Work with the current timeframe only
   TIMEFRAMES_MODE_LIST,                              // Work with the specified timeframe list
   TIMEFRAMES_MODE_ALL                                // Work with the full timeframe list
  };
//+------------------------------------------------------------------+
//| Choice "Yes"/"No"                                                |
//+------------------------------------------------------------------+
enum ENUM_INPUT_YES_NO
  {
   INPUT_NO  = 0,                                     // No
   INPUT_YES = 1                                      // Yes
  };
//+------------------------------------------------------------------+
//| Select color themes                                              |
//+------------------------------------------------------------------+
enum ENUM_COLOR_THEME
  {
   COLOR_THEME_BLUE_STEEL,                            // Blue steel
   COLOR_THEME_LIGHT_CYAN_GRAY,                       // Light cyan gray
  };
//+------------------------------------------------------------------+
#endif 
//+------------------------------------------------------------------+

Isso nos permitirá selecionar o esquema de cores desejado ao iniciar o programa. No futuro, vamos adicionar isso e a escolha de estilos para desenho de objetos e tipos de construção.

Classe do objeto Forma

Um objeto-forma é uma versão mais avançada de um objeto-elemento gráfico. A forma permitirá o desenho de bordas "grossas" e outras primitivas, anexando outros elementos a ela. Naturalmente, podemos desenhar "manualmente" o que quisermos no elemento, mas a forma permitirá que automatizar esse processo.

Na pasta E:\MetaQuotes\MetaTrader 5\MQL5\Include\DoEasy\Objects\Graph\ criamos o novo arquivo Form.mqh da classe CForm. A classe deve ser herdada do objeto-elemento gráfico, e o arquivo do objeto-elemento deve ser anexado:

//+------------------------------------------------------------------+
//|                                                         Form.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/pt/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "GCnvElement.mqh"
//+------------------------------------------------------------------+
//| Form object class                                                |
//+------------------------------------------------------------------+
class CForm : public CGCnvElement
  {
  }

Na seção privada da classe vamos declarar os objetos, variáveis e métodos auxiliares de classe necessários:

//+------------------------------------------------------------------+
//| Form object class                                                |
//+------------------------------------------------------------------+
class CForm : public CGCnvElement
  {
private:
   CArrayObj         m_list_elements;                          // List of attached elements
   CGCnvElement     *m_shadow_obj;                             // Pointer to the shadow object
   color             m_color_frame;                            // Form frame color
   color             m_color_shadow;                           // Form shadow color
   int               m_frame_width_left;                       // Form frame width to the left
   int               m_frame_width_right;                      // Form frame width to the right
   int               m_frame_width_top;                        // Form frame width at the top
   int               m_frame_width_bottom;                     // Form frame width at the bottom

//--- Initialize the variables
   void              Initialize(void);

//--- Create a new graphical object
   CGCnvElement     *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                      const int element_num,
                                      const string name,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool movable,
                                      const bool activity);
   
public:

A seção pública da classe contém os métodos padrão para objetos da biblioteca e alguns construtores: por padrão outros que permitem criar um objeto-forma no gráfico especificado e na subjanela especificada, na subjanela especificada do gráfico atual e no gráfico atual na janela principal:

public:
   //--- Constructors
                     CForm(const long chart_id,
                           const int subwindow,
                           const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
                     CForm(const int subwindow,
                           const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
                     CForm(const string name,
                           const int x,
                           const int y,
                           const int w,
                           const int h);
                     CForm() { this.Initialize(); }
//--- Destructor
                    ~CForm();
                           
//--- Supported form properties (1) integer and (2) string ones
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)  { return true; }
   
//--- Return (1) the list of attached objects and (2) the shadow object
   CArrayObj        *GetList(void)                                            { return &this.m_list_elements;  }
   CGCnvElement     *GetShadowObj(void)                                       { return this.m_shadow_obj;      }

Em seguida, são declarados os métodos para trabalhar com o objeto-forma:

//--- Set the form (1) color scheme and (2) style
   virtual void      SetColorTheme(const ENUM_COLOR_THEMES theme,const uchar opacity);
   virtual void      SetFormStyle(const ENUM_FORM_STYLE style,
                                  const ENUM_COLOR_THEMES theme,
                                  const uchar opacity,
                                  const bool shadow=false,
                                  const bool redraw=false);
   
//--- Create a new attached element
   bool              CreateNewElement(const int element_num,
                                      const string name,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool movable,
                                      const bool activity);

//--- Create a shadow object
   void              CreateShadow(const uchar opacity);
//--- Draw an object shadow
   void              DrawShadow(const uchar opacity);

//--- Draw the form frame
   void              DrawFormFrame(const int wd_top,                          // Frame upper segment width
                                   const int wd_bottom,                       // Frame lower segment width
                                   const int wd_left,                         // Frame left segment width
                                   const int wd_right,                        // Frame right segment width
                                   const color colour,                        // Frame color
                                   const uchar opacity,                       // Frame opacity
                                   const ENUM_FRAME_STYLE style);             // Frame style
//--- Draw a simple frame
   void              DrawFrameSimple(const int x,                             // X coordinate relative to the form
                                     const int y,                             // Y coordinate relative to the form
                                     const int width,                         // Frame width
                                     const int height,                        // Frame height
                                     const int wd_top,                        // Frame upper segment width
                                     const int wd_bottom,                     // Frame lower segment width
                                     const int wd_left,                       // Frame left segment width
                                     const int wd_right,                      // Frame right segment width
                                     const color colour,                      // Frame color
                                     const uchar opacity);                    // Frame opacity
//--- Draw a flat frame
   void              DrawFrameFlat(const int x,                               // X coordinate relative to the form
                                   const int y,                               // Y coordinate relative to the form
                                   const int width,                           // Frame width
                                   const int height,                          // Frame height
                                   const int wd_top,                          // Frame upper segment width
                                   const int wd_bottom,                       // Frame lower segment width
                                   const int wd_left,                         // Frame left segment width
                                   const int wd_right,                        // Frame right segment width
                                   const color colour,                        // Frame color
                                   const uchar opacity);                      // Frame opacity

//--- Draw an embossed (convex) frame
   void              DrawFrameBevel(const int x,                              // X coordinate relative to the form
                                    const int y,                              // Y coordinate relative to the form
                                    const int width,                          // Frame width
                                    const int height,                         // Frame height
                                    const int wd_top,                         // Frame upper segment width
                                    const int wd_bottom,                      // Frame lower segment width
                                    const int wd_left,                        // Frame left segment width
                                    const int wd_right,                       // Frame right segment width
                                    const color colour,                       // Frame color
                                    const uchar opacity);                     // Frame opacity

//--- Draw an embossed (concave) frame
   void              DrawFrameStamp(const int x,                              // X coordinate relative to the form
                                    const int y,                              // Y coordinate relative to the form
                                    const int width,                          // Frame width
                                    const int height,                         // Frame height
                                    const int wd_top,                         // Frame upper segment width
                                    const int wd_bottom,                      // Frame lower segment width
                                    const int wd_left,                        // Frame left segment width
                                    const int wd_right,                       // Frame right segment width
                                    const color colour,                       // Frame color
                                    const uchar opacity);                     // Frame opacity

//--- Draw a simple field
   void              DrawFieldFlat(const int x,                               // X coordinate relative to the form
                                   const int y,                               // Y coordinate relative to the form
                                   const int width,                           // Field width
                                   const int height,                          // Field height
                                   const color colour,                        // Field color
                                   const uchar opacity);                      // Field opacity

//--- Draw an embossed (convex) field
   void              DrawFieldBevel(const int x,                              // X coordinate relative to the form
                                    const int y,                              // Y coordinate relative to the form
                                    const int width,                          // Field width
                                    const int height,                         // Field height
                                    const color colour,                       // Field color
                                    const uchar opacity);                     // Field opacity

//--- Draw an embossed (concave) field
   void              DrawFieldStamp(const int x,                              // X coordinate relative to the form
                                    const int y,                              // Y coordinate relative to the form
                                    const int width,                          // Field width
                                    const int height,                         // Field height
                                    const color colour,                       // Field color
                                    const uchar opacity);                     // Field opacity
   
//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- (1) Set and (2) get the form frame color
   void              SetColorFrame(const color colour)                        { this.m_color_frame=colour;  }
   color             ColorFrame(void)                                   const { return this.m_color_frame;  }
//--- (1) Set and (2) return the form shadow color
   void              SetColorShadow(const color colour)                       { this.m_color_shadow=colour; }
   color             ColorShadow(void)                                  const { return this.m_color_shadow; }

  };
//+------------------------------------------------------------------+

Vamos dar uma olhada mais de perto nos métodos declarados.

Construtor indicando o ID do gráfico e da subjanela:

//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CForm::CForm(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_FORM,chart_id,subwindow,name,x,y,w,h)
  {
   this.Initialize();
  }
//+------------------------------------------------------------------+

Ao construtor são transferidos o ID do gráfico, o número da subjanela na qual precisa ser criado o objeto-forma, seu nome, as coordenadas do canto superior esquerdo da forma e suas dimensões. Na lista de inicialização, chamamos o construtor da classe do objeto-elemento, indicando o tipo do objeto "Forma". No corpo da classe, chamamos o método de inicialização.

Construtor no gráfico atual indicando a subjanela:

//+------------------------------------------------------------------+
//| Current chart constructor specifying the subwindow               |
//+------------------------------------------------------------------+
CForm::CForm(const int subwindow,
             const string name,
             const int x,
             const int y,
             const int w,
             const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,::ChartID(),subwindow,name,x,y,w,h)
  {
   this.Initialize();
  }
//+------------------------------------------------------------------+

O número da subjanela onde queremos criar o objeto-forma (o gráfico é o atual), o nome do objeto-forma, as coordenadas do canto superior esquerdo da forma e suas dimensões. Na lista de inicialização, chamamos o construtor da classe do objeto-elemento, indicando o tipo do objeto "Forma" e o identificador do gráfico atual. No corpo da classe, chamamos o método de inicialização.

Construtor no gráfico atual na janela principal do gráfico:

//+------------------------------------------------------------------+
//| Constructor on the current chart in the main chart window        |
//+------------------------------------------------------------------+
CForm::CForm(const string name,
             const int x,
             const int y,
             const int w,
             const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,::ChartID(),0,name,x,y,w,h)
  {
   this.Initialize();
  }
//+------------------------------------------------------------------+

Ao construtor são passados o nome do objeto-forma, as coordenadas do canto superior esquerdo da forma e suas dimensões. Na lista de inicialização, chamamos o construtor da classe do objeto-elemento indicando o tipo do objeto "Forma", o identificador do gráfico atual e o número da janela principal (0). No corpo da classe, chamamos o método de inicialização.

No destruidor da classe verificamos a validade do ponteiro do objeto sombra e excluímos o objeto se ele existir:

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CForm::~CForm()
  {
   if(m_shadow_obj!=NULL)
      delete m_shadow_obj;
  }
//+------------------------------------------------------------------+

Método de inicialização de variáveis:

//+------------------------------------------------------------------+
//| Initialize the variables                                         |
//+------------------------------------------------------------------+
void CForm::Initialize(void)
  {
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
   this.m_frame_width_right=2;
   this.m_frame_width_left=2;
   this.m_frame_width_top=2;
   this.m_frame_width_bottom=2;
  }
//+------------------------------------------------------------------+

Aqui: limpamos a lista de elementos anexados à forma, definimos o sinalizador de lista classificada e especificamos os valores padrão para o ponteiro para o objeto de sombra (NULL), sinalizador de desenho sombra (false) e tamanhos das bordas da forma (2 pixels de cada lado).

Método privado 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 obj_name,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool movable,
                                      const bool activity)
  {
   int pos=::StringLen(::MQLInfoString(MQL_PROGRAM_NAME));
   string pref=::StringSubstr(NameObj(),pos+1);
   string name=pref+"_"+obj_name;
   CGCnvElement *element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity);
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name);
   return element;
  }
//+------------------------------------------------------------------+

O método recebe todos os parâmetros necessários para criar um novo objeto: seu tipo, número na lista de objetos anexados, nome, coordenadas e dimensões, cor, opacidade e sinalizadores de mobilidade e atividade do objeto.

No corpo da classe, extraímos o nome da extremidade a partir do nome do objeto (o nome consiste no nome do programa e no nome do objeto quando é criado). Precisamos extrair o nome do objeto quando ele é criado e adicionar o nome passado ao método para ele.
Assim, a partir, por exemplo, do nome "Program_name_Form01" nós extraímos a substring "Form01" e adicionamos o nome passado ao método a esta string. Se criarmos um objeto de sombra e passarmos o nome "Sombra", o nome do objeto será "Form01_Sombra" e o nome final do objeto criado será "Program_name_Form01_Sombra".

A seguir, criamos um novo objeto com a indicação de tipo e com os parâmetros do gráfico. Retornamos ao método um ponteiro para o objeto criado ou NULL em caso de falha.

Método que cria um novo elemento anexado:

//+------------------------------------------------------------------+
//| Create a new attached element                                    |
//+------------------------------------------------------------------+
bool CForm::CreateNewElement(const int element_num,
                             const string element_name,
                             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 *obj=this.CreateNewGObject(GRAPH_ELEMENT_TYPE_ELEMENT,element_num,element_name,x,y,w,h,colour,opacity,movable,activity);
   if(obj==NULL)
      return false;
   this.m_list_elements.Sort(SORT_BY_CANV_ELEMENT_NAME_OBJ);
   int index=this.m_list_elements.Search(obj);
   if(index>WRONG_VALUE)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_OBJ_ALREADY_IN_LIST),": ",obj.NameObj());
      delete obj;
      return false;
     }
   if(!this.m_list_elements.Add(obj))
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST),": ",obj.NameObj());
      delete obj;
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

O método cria um novo objeto-elemento gráfico usando o método acima e o adiciona à lista de objetos anexados ao objeto-forma. Se um novo objeto não pôde ser criado ou não foi possível adicioná-lo à lista de objetos anexados, uma mensagem de erro é exibida e é retornado false. Após a criação bem-sucedida de um novo elemento e adicionando-o à lista, é retornado true.

Método que cria o objeto de sombra:

//+------------------------------------------------------------------+
//| Create the shadow object                                         |
//+------------------------------------------------------------------+
void CForm::CreateShadow(const uchar opacity)
  {
//--- If the shadow flag is disabled, exit
   if(!this.m_shadow)
      return;
//--- Calculate the shadow object coordinates according to the offset from the top and left
   int x=this.CoordX()-OUTER_AREA_SIZE;
   int y=this.CoordY()-OUTER_AREA_SIZE;
//--- Calculate the width and height in accordance with the top, bottom, left and right offsets
   int w=this.Width()+OUTER_AREA_SIZE*2;
   int h=this.Height()+OUTER_AREA_SIZE*2;
//--- Create a new element object and set the pointer to it in the variable
   this.m_shadow_obj=this.CreateNewGObject(GRAPH_ELEMENT_TYPE_ELEMENT,-1,"Shadow",x,y,w,h,this.m_chart_color_bg,opacity,Movable(),false);
   if(this.m_shadow_obj==NULL)
      return;
//--- Move the form object to the foreground
   this.BringToTop();
  }
//+------------------------------------------------------------------+

A lógica do método é comentada em sua listagem. Resumindo: como o objeto-elemento, onde é necessário desenhar a sombra, deve ser maior do que o objeto-forma para o qual foi criado (acima, abaixo, à esquerda e à direita, precisamos de espaço livre para desenhar a sombra), o tamanho do novo objeto é calculado de acordo com os valores de substituição da macro OUTER_AREA_SIZE.
Depois de criar um objeto com sucesso, ele se eleva automaticamente acima do objeto-forma no qual foi criado. Por isso, precisamos forçar o objeto-forma para a frente - faremos isso no final do método.

Método de desenho de sombra:

//+------------------------------------------------------------------+
//| Draw the shadow                                                  |
//+------------------------------------------------------------------+
void CForm::DrawShadow(const uchar opacity)
  {
//--- If the shadow flag is disabled, exit
   if(!this.m_shadow)
      return;
//--- Calculate rectangle coordinates relative to the shadow object borders
   int x=OUTER_AREA_SIZE+1;
   int y=OUTER_AREA_SIZE+1;
//--- Draw a filled rectangle starting from the calculated coordinates and having the size of the current form object
   m_shadow_obj.DrawRectangleFill(x,y,x+Width(),y+Height(),this.ColorShadow(),opacity);
//--- Update the shadow object for displaying changes
   m_shadow_obj.Update();
   return;
  }
//+------------------------------------------------------------------+

A lógica do método é comentada em seu código. Este método é atualmente apenas um modelo para a criação de um método completo para desenhar sombras de objetos. No momento, o método simplesmente desenha "sob" o objeto atual no objeto de elemento criado para desenhar sombras, um retângulo simples deslocado para a direita e para baixo.

Método de configuração do esquema de cores:

//+------------------------------------------------------------------+
//| Set a color scheme                                               |
//+------------------------------------------------------------------+
void CForm::SetColorTheme(const ENUM_COLOR_THEMES theme,const uchar opacity)
  {
   this.SetOpacity(opacity);
   this.SetColorBackground(array_color_themes[theme][COLOR_THEME_COLOR_FORM_BG]);
   this.SetColorFrame(array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME]);
   this.SetColorShadow(array_color_themes[theme][COLOR_THEME_COLOR_FORM_SHADOW]);
  }
//+------------------------------------------------------------------+

O método é usado para definir o tema de cores especificado para o objeto. Ao método são transferidos o tema necessário e o valor de opacidade do objeto-forma. Em seguida, relativamente à forma, o valor de opacidade, a cor de fundo, a cor da borda e a cor da sombra são definidos a partir dos valores registrados na matriz de temas de cores que criamos acima.

Método para definir o estilo da forma:

//+------------------------------------------------------------------+
//| Set the form style                                               |
//+------------------------------------------------------------------+
void CForm::SetFormStyle(const ENUM_FORM_STYLE style,
                         const ENUM_COLOR_THEMES theme,
                         const uchar opacity,
                         const bool shadow=false,
                         const bool redraw=false)
  {
//--- Set opacity parameters and the size of the form frame side
   this.m_shadow=shadow;
   this.m_frame_width_top=array_form_style[style][FORM_STYLE_FRAME_WIDTH_TOP];
   this.m_frame_width_bottom=array_form_style[style][FORM_STYLE_FRAME_WIDTH_BOTTOM];
   this.m_frame_width_left=array_form_style[style][FORM_STYLE_FRAME_WIDTH_LEFT];
   this.m_frame_width_right=array_form_style[style][FORM_STYLE_FRAME_WIDTH_RIGHT];
//--- Set a color scheme
   this.SetColorTheme(theme,opacity);
//--- Create the shadow object and draw a simple distinct shadow
   this.CreateShadow((uchar)array_form_style[style][FORM_STYLE_FRAME_SHADOW_OPACITY]);
   this.DrawShadow((uchar)array_form_style[style][FORM_STYLE_FRAME_SHADOW_OPACITY]);
//--- Fill in the form background with color and opacity
   this.Erase(this.ColorBackground(),this.Opacity());
//--- Depending on the selected form style, draw the corresponding form frame and the outer bounding frame
   switch(style)
     {
      case FORM_STYLE_BEVEL   :
        this.DrawFormFrame(this.m_frame_width_top,this.m_frame_width_bottom,this.m_frame_width_left,this.m_frame_width_right,this.ColorFrame(),this.Opacity(),FRAME_STYLE_BEVEL);
        this.DrawRectangle(0,0,Width()-1,Height()-1,array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME_OUTER],this.Opacity());
        break;
      //---FORM_STYLE_FLAT
      default:
        this.DrawFormFrame(this.m_frame_width_top,this.m_frame_width_bottom,this.m_frame_width_left,this.m_frame_width_right,this.ColorFrame(),this.Opacity(),FRAME_STYLE_FLAT);
        this.DrawRectangle(0,0,Width()-1,Height()-1,array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME_OUTER],this.Opacity());
        break;
     }
  }
//+------------------------------------------------------------------+

A lógica do método é comentada em sua listagem.

Na verdade, esse método é um exemplo de criação de um objeto-forma com os parâmetros necessários.

Método para desenhar a borda da forma:

//+------------------------------------------------------------------+
//| Draw the form frame                                               |
//+------------------------------------------------------------------+
void CForm::DrawFormFrame(const int wd_top,              // Frame upper segment width
                          const int wd_bottom,           // Frame lower segment width
                          const int wd_left,             // Frame left segment width
                          const int wd_right,            // Frame right segment width
                          const color colour,            // Frame color
                          const uchar opacity,           // Frame opacity
                          const ENUM_FRAME_STYLE style)  // Frame style
  {
//--- Depending on the passed frame style
   switch(style)
     {
      //--- draw a dimensional (convex) frame
      case FRAME_STYLE_BEVEL :
         DrawFrameBevel(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
        break;
      //--- draw a dimensional (concave) frame
      case FRAME_STYLE_STAMP :
         DrawFrameStamp(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
        break;
      //--- draw a flat frame
      case FRAME_STYLE_FLAT :
         DrawFrameFlat(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
        break;
      //--- draw a simple frame
      default:
        //---FRAME_STYLE_SIMPLE
         DrawFrameSimple(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
        break;
     }
  }
//+------------------------------------------------------------------+

Desenhamos a borda da forma, dependendo do estilo da borda.

Método para desenhar uma borda simples:

//+------------------------------------------------------------------+
//| Draw a simple frame                                              |
//+------------------------------------------------------------------+
void CForm::DrawFrameSimple(const int x,           // X coordinate relative to the form
                            const int y,           // Y coordinate relative to the form
                            const int width,       // Frame width
                            const int height,      // Frame height
                            const int wd_top,      // Frame upper segment width
                            const int wd_bottom,   // Frame lower segment width
                            const int wd_left,     // Frame left segment width
                            const int wd_right,    // Frame right segment width
                            const color colour,    // Frame color
                            const uchar opacity)   // Frame opacity
  {
//--- Set rectangle coordinates
   int x1=x, y1=y;
   int x2=x1+width-1;
   int y2=y1+height-1;
//--- Draw the first rectangle
   CGCnvElement::DrawRectangle(x1,y1,x2,y2,colour,opacity);
//--- If the frame width exceeds 1 on all sides, draw the second rectangle
   if(wd_left>1 || wd_right>1 || wd_top>1 || wd_bottom>1)
      CGCnvElement::DrawRectangle(x1+wd_left-1,y1+wd_top-1,x2-wd_right+1,y2-wd_bottom+1,colour,opacity);
//--- Search for "voids" between the lines of two rectangles and fill them with color
   if(wd_left>2 && wd_right>2 && wd_top>2 && wd_bottom>2)
      this.Fill(x1+1,y1+1,colour,opacity);
   else if(wd_left>2 && wd_top>2)
      this.Fill(x1+1,y1+1,colour,opacity);
   else if(wd_right>2 && wd_bottom>2)
      this.Fill(x2-1,y2-1,colour,opacity);
   else if(wd_left<3 && wd_right<3)
     {
      if(wd_top>2)
         this.Fill(x1+1,y1+1,colour,opacity);
      if(wd_bottom>2)
         this.Fill(x1+1,y2-1,colour,opacity);
     }
   else if(wd_top<3 && wd_bottom<3)
     {
      if(wd_left>2)
         this.Fill(x1+1,y1+1,colour,opacity);
      if(wd_right>2)
         this.Fill(x2-1,y1+1,colour,opacity);
     }
  }
//+------------------------------------------------------------------+

A lógica do método é comentada no código. Resumindo: desenhamos dois retângulos - um dentro do outro. Se houver vazios entre os retângulos desenhados nos lugares que formam os lados da futura borda (os lados dos retângulos não se tocam), nós os preenchemos com a cor com a qual os retângulos foram desenhados.

Método para desenhar uma borda plana:

//+------------------------------------------------------------------+
//| Draw the flat frame                                              |
//+------------------------------------------------------------------+
void CForm::DrawFrameFlat(const int x,
                          const int y,
                          const int width,
                          const int height,
                          const int wd_top,
                          const int wd_bottom,
                          const int wd_left,
                          const int wd_right,
                          const color colour,
                          const uchar opacity)
  {
//--- Draw a simple frame
   this.DrawFrameSimple(x,y,width,height,wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
//--- If the width of the frame top and bottom exceeds one pixel
   if(wd_top>1 && wd_bottom>1)
     {
      //--- Darken the horizontal sides of the frame
      for(int i=0;i<width;i++)
        {
         this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.05));
         this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.07));
        }
     }
//--- If the width of the frame left and right sides exceeds one pixel
   if(wd_left>1 && wd_right>1)
     {
      //--- Darken the vertical sides of the frame
      for(int i=1;i<height-1;i++)
        {
         this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),-0.01));
         this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),-0.02));
        }
     }
  }
//+------------------------------------------------------------------+

Método para desenhar uma borda em relevo (convexa):

//+------------------------------------------------------------------+
//| Draw an embossed (convex) frame                                  |
//+------------------------------------------------------------------+
void CForm::DrawFrameBevel(const int x,
                           const int y,
                           const int width,
                           const int height,
                           const int wd_top,
                           const int wd_bottom,
                           const int wd_left,
                           const int wd_right,
                           const color colour,
                           const uchar opacity)
  {
//--- Draw a simple frame
   this.DrawFrameSimple(x,y,width,height,wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
//--- If the width of the frame top and bottom exceeds one pixel
   if(wd_top>1 && wd_bottom>1)
     {
      //--- Lighten and darken the required sides of the frame edges
      for(int i=0;i<width;i++)
        {
         this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),0.25));
         this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.2));
        }
      for(int i=wd_left;i<width-wd_right;i++)
        {
         this.m_canvas.PixelSet(x+i,y+wd_top-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+wd_top-1),-0.2));
         this.m_canvas.PixelSet(x+i,y+height-wd_bottom,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-wd_bottom),0.1));
        }
     }
//--- If the width of the frame left and right sides exceeds one pixel
   if(wd_left>1 && wd_right>1)
     {
      //--- Lighten and darken the required sides of the frame edges
      for(int i=1;i<height-1;i++)
        {
         this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+i),0.1));
         this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+i),-0.1));
        }
      for(int i=wd_top;i<height-wd_bottom;i++)
        {
         this.m_canvas.PixelSet(x+wd_left-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+wd_left-1,y+i),-0.1));
         this.m_canvas.PixelSet(x+width-wd_right,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-wd_right,y+i),0.1));
        }
     }
  }
//+------------------------------------------------------------------+

Método que desenha uma borda em relevo (côncava):

//+------------------------------------------------------------------+
//| Draw an embossed (concave) frame                                 |
//+------------------------------------------------------------------+
void CForm::DrawFrameStamp(const int x,
                           const int y,
                           const int width,
                           const int height,
                           const int wd_top,
                           const int wd_bottom,
                           const int wd_left,
                           const int wd_right,
                           const color colour,
                           const uchar opacity)
  {
//--- Draw a simple frame
   this.DrawFrameSimple(x,y,width,height,wd_top,wd_bottom,wd_left,wd_right,colour,opacity);
//--- If the width of the frame top and bottom exceeds one pixel
   if(wd_top>1 && wd_bottom>1)
     {
      //--- Lighten and darken the required sides of the frame edges
      for(int i=0;i<width;i++)
        {
         this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.25));
         this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),0.2));
        }
      for(int i=wd_left;i<width-wd_right;i++)
        {
         this.m_canvas.PixelSet(x+i,y+wd_top-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+wd_top-1),0.2));
         this.m_canvas.PixelSet(x+i,y+height-wd_bottom,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-wd_bottom),-0.25));
        }
     }
//--- If the width of the frame left and right sides exceeds one pixel
   if(wd_left>1 && wd_right>1)
     {
      //--- Lighten and darken the required sides of the frame edges
      for(int i=1;i<height-1;i++)
        {
         this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+i),-0.1));
         this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+i),0.2));
        }
      for(int i=wd_top;i<height-wd_bottom;i++)
        {
         this.m_canvas.PixelSet(x+wd_left-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+wd_left-1,y+i),0.2));
         this.m_canvas.PixelSet(x+width-wd_right,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-wd_right,y+i),-0.2));
        }
     }
  }
//+------------------------------------------------------------------+

Métodos que desenham campos (simples e em relevo):

//+------------------------------------------------------------------+
//| Draw a simple field                                              |
//+------------------------------------------------------------------+
void CForm::DrawFieldFlat(const int x,const int y,const int width,const int height,const color colour,const uchar opacity)
  {
//--- Draw a filled rectangle
   CGCnvElement::DrawRectangleFill(x,y,x+width-1,y+height-1,colour,opacity);
//--- Darken all its edges
   for(int i=0;i<width;i++)
     {
      this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.05));
      this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.05));
     }
   for(int i=1;i<height-1;i++)
     {
      this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),-0.05));
      this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),-0.05));
     }
  }
//+------------------------------------------------------------------+
//| Draw an embossed (convex) field                                  |
//+------------------------------------------------------------------+
void CForm::DrawFieldBevel(const int x,const int y,const int width,const int height,const color colour,const uchar opacity)
  {
//--- Draw a filled rectangle
   CGCnvElement::DrawRectangleFill(x,y,x+width-1,y+height-1,colour,opacity);
//--- Lighten its top and left and darken its bottom and right
   for(int i=0;i<width;i++)
     {
      this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),0.1));
      this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.1));
     }
   for(int i=1;i<height-1;i++)
     {
      this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),0.05));
      this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),-0.05));
     }
  }
//+------------------------------------------------------------------+
//| Draw an embossed (concave) field                                 |
//+------------------------------------------------------------------+
void CForm::DrawFieldStamp(const int x,const int y,const int width,const int height,const color colour,const uchar opacity)
  {
//--- Draw a filled rectangle
   CGCnvElement::DrawRectangleFill(x,y,x+width-1,y+height-1,colour,opacity);
//--- Darken its top and left and lighten its bottom and right
   for(int i=0;i<width;i++)
     {
      this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.1));
      this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),0.1));
     }
   for(int i=1;i<height-1;i++)
     {
      this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),-0.05));
      this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),0.05));
     }
  }
//+------------------------------------------------------------------+

A lógica de todos os métodos acima é quase idêntica e comentada no código do método. Acho que os métodos não vão causar dúvidas e mal-entendidos. Em qualquer caso, tudo pode ser discutido nos comentários ao artigo.

Assim concluímos a criação do objeto-forma para hoje.


Teste

Não haverá testes "encantadores" hoje :) Hoje vamos simplesmente criar duas formas diferentes com estilos de construção e temas de cores diferentes. Após a criação, a cada campo adicionamos: um campo côncavo à forma superio e um campo côncavo volumétrico translúcido à segunda forma.

Para o teste, vamos pegar o Expert Advisor do último artigo e salvá-lo na nova pasta \MQL5\Experts\TestDoEasy\Part76\ com o novo nome TestDoEasyPart76.mq5.

Integramos no Expert Advisor o arquivo do objeto-forma da biblioteca e renomeamos a lista dos objetos-elementos na lista de objetos-formas:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart76.mq5 |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/pt/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/pt/users/artmedia70"
#property version   "1.00"
//--- includes
#include <Arrays\ArrayObj.mqh>
#include <DoEasy\Services\Select.mqh>
#include <DoEasy\Objects\Graph\Form.mqh>
//--- defines
#define        FORMS_TOTAL (2)   // Number of created forms
//--- input parameters
sinput   bool  InpMovable  = true;  // Movable flag
//--- global variables
CArrayObj      list_forms;
//+------------------------------------------------------------------+

No manipulador OnInit() criamos duas formas e desenhamos nelas os campos côncavos:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set the permissions to send cursor movement and mouse scroll events
   ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_MOVE,true);
   ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_WHEEL,true);
//--- Set EA global variables

//--- Create the specified number of form objects
   list_forms.Clear();
   int total=FORMS_TOTAL;
   for(int i=0;i<total;i++)
     {
      //--- When creating an object, pass all the required parameters to it
      CForm *form=new CForm("Form_0"+(string)(i+1),300,40+(i*80),100,70);
      if(form==NULL)
         continue;
      //--- Set activity and moveability flags for the form
      form.SetActive(true);
      form.SetMovable(false);
      //--- Set the form ID equal to the loop index and the index in the list of objects
      form.SetID(i);
      form.SetNumber(0);   // (0 - main form object) Auxiliary objects may be attached to the main one. The main object is able to manage them
      //--- Set the full opacity for the top form and the partial opacity for the bottom one
      uchar opacity=(i==0 ? 255 : 250);
      //--- Set the form style and its color theme depending on the loop index
      ENUM_FORM_STYLE style=(ENUM_FORM_STYLE)i;
      ENUM_COLOR_THEMES theme=(ENUM_COLOR_THEMES)i;
      //--- Set the form style and theme
      form.SetFormStyle(style,theme,opacity,true);
      //--- If this is the first (top) form
      if(i==0)
        {
         //--- Draw a concave field slightly shifted from the center of the form downwards
         form.DrawFieldStamp(3,10,form.Width()-6,form.Height()-13,form.ColorBackground(),form.Opacity());
         form.Update(true);
        }
      //--- If this is the second (bottom) form
      if(i==1)
        {
         //--- Draw a concave semi-transparent "tainted glass" field in the center
         form.DrawFieldStamp(10,10,form.Width()-20,form.Height()-20,clrWheat,200);
         form.Update(true);
        }
      //--- Add objects to the list
      if(!list_forms.Add(form))
        {
         delete form;
         continue;
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Do manipulador OnChartEvent() removemos todo o processamento de cliques do mouse objetos:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- If clicking on an object
   if(id==CHARTEVENT_OBJECT_CLICK)
     {

     }
  }
//+------------------------------------------------------------------+

Vamos compilar o Expert Advisor e executá-lo no gráfico:


Como podemos ver, simplesmente especificando o estilo e o tema de cores desejados, criamos duas formas com cores diferentes para os componentes e estilo de desenho.

O que vem agora?

No próximo artigo, continuaremos o desenvolvimento do objeto forma e complementaremos sua funcionalidade.

Todos os arquivos da versão atual da biblioteca e o arquivo do EA de teste para MQL5 estão anexados abaixo. Você pode baixá-los e testar tudo sozinho.
Se você tiver perguntas, comentários e sugestões, poderá expressá-los nos comentários do artigo.

Complementos

*Artigos desta série:

Gráficos na biblioteca DoEasy (Parte 73): objeto-forma de um elemento gráfico
Gráficos na biblioteca DoEasy (Parte 74): elemento gráfico básico baseado na classe CCanvas
Gráficos na biblioteca DoEasy (Parte 75): métodos para trabalhar com primitivos e texto num elemento gráfico básico

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/9553

Arquivos anexados |
MQL5.zip (3980.07 KB)
Gráficos na biblioteca DoEasy (Parte 77): classe do objeto Sombra Gráficos na biblioteca DoEasy (Parte 77): classe do objeto Sombra
Neste artigo, criaremos uma classe separada para o objeto de sombra (um herdeiro do objeto de elemento gráfico) e também adicionaremos a capacidade de ocupar o fundo do objeto com um preenchimento gradiente.
Análise de Cluster (Parte I): usando a inclinação das linhas indicadoras Análise de Cluster (Parte I): usando a inclinação das linhas indicadoras
A análise de cluster é um dos elementos mais importantes da inteligência artificial. Neste artigo, tento usar uma análise de cluster aplicada na inclinação de um indicador para obter patamares que determinarão se o mercado está lateralizado ou mantém uma tendência.
Padrões com exemplos (Parte I): Topo múltiplo Padrões com exemplos (Parte I): Topo múltiplo
Com este artigo começamos um ciclo em que consideraremos padrões de reversão no âmbito da negociação algorítmica. Iniciamos examinando a primeira e mais interessante família de padrões desse tipo que se originam dos chamados topo duplo e fundo duplo.
Combinatória e teoria da probabilidade para negociação (Parte I): fundamentos Combinatória e teoria da probabilidade para negociação (Parte I): fundamentos
Nesta série de artigos, procuraremos uma aplicação prática da teoria da probabilidade para descrever o processo de negociação e precificação. No primeiro artigo, conheceremos os fundamentos da combinatória e da teoria da probabilidade, e analisaremos o primeiro exemplo de aplicação de fractais no âmbito desta última.