Interfaces Gráficas IV: Elementos Informativos da Interface (Capítulo 1)
Conteúdo
- Introdução
- O Elemento Barra de Status
- Teste da Barra de Status
- O Elemento Dica de Contexto
- Teste da Dica de Contexto
- Array Privado para a Dica de Contexto
- Conclusão
Introdução
O primeiro artigo Interfaces gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1) considera em detalhes a finalidade desta biblioteca. A lista completa dos links para os artigos se encontra no final de cada capítulo da série. Lá, você também pode encontrar e baixar a versão completa da biblioteca, no estágio de desenvolvimento atual. Os arquivos devem estar localizados nas mesmas pastas que o arquivo baixado.
No atual estágio de desenvolvimento, a biblioteca para a criação de interfaces gráficas contém um formulário e vários controles que podem ser ligados a ele. Foi mencionado antes que um dos artigos futuros seria dedicado ao modo multi-janela. Agora, nós já temos tudo preparado para considerar tal questão, desse modo, nós vamos lidar com isso no capítulo seguinte. Neste capítulo, nós vamos escrever as classes para criar os elementos de interface barra de status e dica de contexto.
O Elemento Barra de Status
A barra de status pertence aos elementos informativos da interface gráfica. Este elemento é usado para uma entrada rápida de dados importantes, detalhes, valores, etc. Os terminais MetaTrader também possuem uma barra de status. Este é composto por várias seções (itens). O primeiro deles apresenta as informações sobre o lugar em que o cursor do mouse está localizado no terminal ou os nomes dos comandos do programa. Há também os itens que exibem as informações de datas e preços quando o cursor do mouse está se movendo sobre a área do gráfico. Alguns itens contêm um menu de contexto, que se abrem pelo clique esquerdo do mouse. O editor de código MetaEditor possui uma barra de status também. Seus itens também exibem os comandos do programa, a localização do cursor (linha/coluna) e o modo de entrada de texto (INS/OVR). Informações mais detalhadas sobre as barras de status dos terminais e do editor de código podem ser encontrados na seção de Ajuda (F1).
Neste artigo, nós vamos criar uma barra de status simples, sem uma opção de ligar os menus de contexto aos seus itens. Semelhante aos outros elementos de interface, a barra de status será composta de vários objetos primitivos:
- Fundo.
- Elementos.
- Linhas de separação.
Fig. 1. Partes integrantes do elemento barra de status.
Crie o arquivo StatusBar.mqh e inclua-o no arquivo WndContainer.mqh para que ele esteja disponível para uso em toda a biblioteca. O código abaixo exibe a classe CStatusBar com os métodos virtuais padrão, que são utilizados em todas as classes de controles.
//+------------------------------------------------------------------+ //| StatusBar.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" //+------------------------------------------------------------------+ //| Classe para criar a barra de status | //+------------------------------------------------------------------+ class CStatusBar : public CElement { private: //--- Ponteiro para o formulário ao qual o elemento está anexado CWindow *m_wnd; //--- public: CStatusBar(void); ~CStatusBar(void); //--- Armazena o ponteiro do formulário void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } //--- public: //--- Manipulador de eventos do gráfico virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) {} //--- Timer virtual void OnEventTimer(void) {} //--- Move o elemento virtual void Moving(const int x,const int y); //--- (1) Exibe, (2) oculta, (3) reseta, (4) remove virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- (1) Definir (2), resetar as prioridades para o clique esquerdo do mouse virtual void SetZorders(void); virtual void ResetZorders(void); }; //+------------------------------------------------------------------+ //| Construtor | //+------------------------------------------------------------------+ CStatusBar::CStatusBar(void) { } //+------------------------------------------------------------------+ //| Destrutor | //+------------------------------------------------------------------+ CStatusBar::~CStatusBar(void) { } //+------------------------------------------------------------------+
Os métodos para o tratamento de eventos OnEvent() e OnEventTimer() não serão utilizados nesta versão para manter a consistência com outras classes de controle.
Agora, nós vamos considerar as propriedades da barra de status. Já que é para ser utilizado os arrays de objetos, haverá propriedades únicas e comuns para eles. Das propriedades únicas temos apenas a largura dos elementos. A lista de propriedades comuns é mais longa e ela é apresentada abaixo.
Propriedades comuns:
- Cor do fundo e do fundo do quadro.
- Cor do texto.
- Cores para a linha de separação.
- Prioridade do botão esquerdo do mouse.
Inicializamos os campos de propriedades dentro do construtor pelos valores predefinidos. No momento da criação do elemento, o usuário pode redefini-los usando os métodos públicos da classe.
class CStatusBar : public CElement { private: //--- Propriedades: // Arrays de propriedades únicas int m_width[]; //--- (1) Cor do fundo e (2) do fundo do quadro color m_area_color; color m_area_border_color; //--- Cor do texto color m_label_color; //--- Prioridade do botão esquerdo do mouse int m_zorder; //--- Cores para as linhas de separação. color m_sepline_dark_color; color m_sepline_light_color; //--- public: //--- (1) Cor do fundo e (2) do fundo do quadro e (3) do texto void AreaColor(const color clr) { m_area_color=clr; } void AreaBorderColor(const color clr) { m_area_border_color=clr; } void LabelColor(const color clr) { m_label_color=clr; } //--- Cores para as linhas de separação. void SeparateLineDarkColor(const color clr) { m_sepline_dark_color=clr; } void SeparateLineLightColor(const color clr) { m_sepline_light_color=clr; } }; //+------------------------------------------------------------------+ //| Construtor | //+------------------------------------------------------------------+ CStatusBar::CStatusBar(void) : m_area_color(C'240,240,240'), m_area_border_color(clrSilver), m_label_color(clrBlack), m_sepline_dark_color(C'160,160,160'), m_sepline_light_color(clrWhite) { //--- Armazena o nome da classe do elemento na classe base CElement::ClassName(CLASS_NAME); //--- Define as prioridades do botão esquerdo do mouse m_zorder=2; }
Vamos considerar os métodos para criar a barra de status. Para criar o fundo, nós vamos usar um objeto primitivo do tipo CRectLabel. O array dinâmico de objetos primitivos do tipo CEdit tem de ser declarado para criar os elementos. A classe CSeparateLine foi escrita anteriormente para a criação das linhas de separação. Esta classe pode ser usada como um elemento de interface independente. Neste caso, ela será usada como parte integrante da barra de status e nós vamos precisar de um conjunto de tais elementos.
Os elementos devem ser adicionados usando o método CStatusBar::AddItem() antes de criar a barra de status, caso contrário, a criação da interface gráfica será encerrada. O número de elementos pode ser obtido chamando o método CStatusBar::ItemsTotal().
//+------------------------------------------------------------------+ //| StatusBar.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "SeparateLine.mqh" //+------------------------------------------------------------------+ //| Classe para criar a barra de status | //+------------------------------------------------------------------+ class CStatusBar : public CElement { private: //--- Objeto para a criação de um botão CRectLabel m_area; CEdit m_items[]; CSeparateLine m_sep_line[]; //--- public: //--- Métodos para a criação da barra de status bool CreateStatusBar(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateArea(void); bool CreateItems(void); bool CreateSeparateLine(const int line_number,const int x,const int y); //--- public: //--- Número de elementos int ItemsTotal(void) const { return(::ArraySize(m_items)); } //--- Adiciona o elemento com propriedades as especificadas antes de criar a barra de status void AddItem(const int width); };
Nós vamos discutir em detalhes apenas o método CStatusBar::CreateItems() para criar os elementos da barra de status. Os demais métodos não possuem nada de novo que nós não tenhamos estudado nas classes de outros controles.
Os elementos são definidos dentro da área de fundo de maneira que eles não obscurecem seu quadro. É por isso que uma margem de pixels é adicionada às coordenadas no início. Em seguida, é realizado uma verificação no número de elementos. Se nenhum elemento foi definido, será impresso uma mensagem no registro e na criação da interface gráfica será encerrada.
Nós faremos com que se a largura do primeiro elemento não tiver sido definida, ele será calculado automaticamente. Supõe-se que a largura da barra de status seja igual à largura do formulário ao qual ele está anexado (apenas dois pixels a menos para ficar dentro da área do formulário). Para obter a largura do primeiro elemento, o tamanho (largura) de outros elementos é para ser adicionado em um loop e este valor é para ser deduzido do valor da largura do formulário.
Em seguida, segue o loop onde os elementos da barra de status são criados e, em seguida, o loop para criar as linhas de separação. As coordenadas para as linhas de separação são calculadas em relação às coordenadas de cada elemento. Uma linha de separação não é criada para o primeiro elemento. Desta forma, o número de linhas de separação sempre será um objeto a menos que o número de elementos.
//+------------------------------------------------------------------+ //| Cria uma lista de elementos de barra de status | //+------------------------------------------------------------------+ bool CStatusBar::CreateItems(void) { int l_w=0; int l_x=m_x+1; int l_y=m_y+1; //--- Obtém o número de elementos int items_total=ItemsTotal(); //--- Se não houver elementos no grupo, reporta e sai if(items_total<1) { ::Print(__FUNCTION__," > Este método era para ser chamado, " "se um grupo conter pelo menos um elemento! Use the CStatusBar::AddItem() method"); return(false); } //--- Se a largura do primeiro elemento não está definida, então... if(m_width[0]<1) { //--- ...calcula ele em relação à largura comum dos outros elementos for(int i=1; i<items_total; i++) l_w+=m_width[i]; //--- m_width[0]=m_wnd.XSize()-l_w-(items_total+2); } //--- Cria o número especificado de elementos for(int i=0; i<items_total; i++) { //--- Elaborando o nome do objeto string name=CElement::ProgramName()+"_statusbar_edit_"+string(i)+"__"+(string)CElement::Id(); //--- Coordenada X l_x=(i>0)? l_x+m_width[i-1] : l_x; //--- Cria um objeto if(!m_items[i].Create(m_chart_id,name,m_subwin,l_x,l_y,m_width[i],m_y_size-2)) return(false); //--- Definindo as propriedades m_items[i].Description(""); m_items[i].TextAlign(ALIGN_LEFT); m_items[i].Font(FONT); m_items[i].FontSize(FONT_SIZE); m_items[i].Color(m_label_color); m_items[i].BorderColor(m_area_color); m_items[i].BackColor(m_area_color); m_items[i].Corner(m_corner); m_items[i].Anchor(m_anchor); m_items[i].Selectable(false); m_items[i].Z_Order(m_zorder); m_items[i].ReadOnly(true); m_items[i].Tooltip("\n"); //--- Margens da borda do painel m_items[i].XGap(l_x-m_wnd.X()); m_items[i].YGap(l_y-m_wnd.Y()); //--- Coordenadas m_items[i].X(l_x); m_items[i].Y(l_y); //--- Tamanho m_items[i].XSize(m_width[i]); m_items[i].YSize(m_y_size-2); //--- Armazena o ponteiro de objeto CElement::AddToArray(m_items[i]); } //--- Criação das linhas de separação for(int i=1; i<items_total; i++) { //--- Coordenada X l_x=m_items[i].X(); //--- Criação de uma linha CreateSeparateLine(i,l_x,l_y+2); } //--- return(true); }
Nós também vamos precisar de um método público para a mudar o texto de cada elemento. Vamos criar tal método e chamá-lo de CStatusBar::ValueToItem(). Ele vai receber como parâmetro o número de índice do elemento e a linha que dever ser refletida nele.
class CStatusBar : public CElement { public: //--- Define o valor do índice especificado void ValueToItem(const int index,const string value); }; //+------------------------------------------------------------------+ //| Define o valor do índice especificado | //+------------------------------------------------------------------+ void CStatusBar::ValueToItem(const int index,const string value) { //--- Verifica se o tamanho do array não excedeu int array_size=::ArraySize(m_items); if(array_size<1 || index<0 || index>=array_size) return; //--- Define o texto passado m_items[index].Description(value); }
Teste da Barra de Status
Agora, nós temos tudo pronto para testar o elemento barra de status. Para o teste, nós usaremos o primeiro EA da parte anterior (3) da série. Mantenha o menu principal e os cinco botões do tipo CIconButton e exclua todos os outros controles. O arquivo StatusBar.mqh já está incluído na biblioteca e sua instância e o método para criação da barra de status podem ser criados na classe personalizada CProgram.
Crie a barra de status que consiste de dois elementos. Nós não iremos definir a largura do primeiro elemento, ele será calculado automaticamente. Após a criação da barra de status, defina o texto «For Help, press F1» no primeiro elemento, como exemplo.
//+------------------------------------------------------------------+ //| Classe para a criação de um aplicativo | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { private: //--- Barra de status CStatusBar m_status_bar; //--- private: //--- Barra de status #define STATUSBAR1_GAP_X (1) #define STATUSBAR1_GAP_Y (175) bool CreateStatusBar(void); }; //+------------------------------------------------------------------+ //| Cria o painel de negociação | //+------------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- Criação do formulário 1 para os controles //--- Criação dos controles: // Menu principal //--- Menus de contexto //--- Criando a barra de status if(!CreateStatusBar()) return(false); //--- Redesenho do gráfico m_chart.Redraw(); return(true); } //+------------------------------------------------------------------+ //| Cria a barra de status | //+------------------------------------------------------------------+ bool CProgram::CreateStatusBar(void) { #define STATUS_LABELS_TOTAL 2 //--- Passa o objeto do painel m_status_bar.WindowPointer(m_window1); //--- Coordenadas int x=m_window1.X()+STATUSBAR1_GAP_X; int y=m_window1.Y()+STATUSBAR1_GAP_Y; //--- Largura int width[]={0,110}; //--- Define as propriedades antes da criação m_status_bar.YSize(24); //--- Especifica o número de componentes e define suas propriedades for(int i=0; i<STATUS_LABELS_TOTAL; i++) m_status_bar.AddItem(width[i]); //--- Cria um controle if(!m_status_bar.CreateStatusBar(m_chart_id,m_subwin,x,y)) return(false); //--- Define o texto no primeiro elemento da barra de status m_status_bar.ValueToItem(0,"For Help, press F1"); //--- Adiciona um objeto ao array comum de grupos de objetos CWndContainer::AddToElementsArray(0,m_status_bar); return(true); }
O segundo elemento da barra de status mostrará a hora local. Por isso, adicione o código ao timer da aplicação, como é mostrado no código abaixo. Isto significa que o elemento será atualizado a cada 500 milissegundos.
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CProgram::OnTimerEvent(void) { CWndEvents::OnTimerEvent(); //--- Atualiza o segundo elemento da barra de status a cada 500 milissegundos static int count=0; if(count<500) { count+=TIMER_STEP_MSC; return; } //--- count=0; m_status_bar.ValueToItem(1,TimeToString(TimeLocal(),TIME_DATE|TIME_SECONDS)); m_chart.Redraw(); }
Se tudo foi feito corretamente, você deve obter o resultado como é mostrado na imagem abaixo:
Fig. 2. Teste do elemento barra de status.
Nós completamos o desenvolvimento da classe para a criação do elemento barra de status. A versão completa pode ser encontrada nos arquivos anexados a este artigo.
O Elemento Dica de Contexto
Agora, nós vamos desenvolver a classe para a criação das dicas de contexto. No quarto capítulo da primeira parte da série, ao considerar as funções para os botões de formulário, foi explicado em detalhes o porquê desse elemento necessitar de uma classe separada. Em suma, as dicas de contexto são necessárias sem limitar o número de símbolos e com a opção de destacar algumas palavras. Para implementar isso, nós vamos usar uma classe para desenhar os elementos. Anteriormente, foi mostrado um exemplo de como os elementos de interface podem ser desenhados. A classe CSeparateLine para criar o elemento linha de separação foi escrito no segundo capítulo da segunda parte desta série. Agora, seguindo o mesmo princípio, iremos desenvolver uma classe para as dicas de contexto.
Fig. 3. Exemplo de uma dica de contexto no Word.
Crie o arquivo Tooltip.mqh e inclua-o no arquivo WndContainer.mqh no qual todos os elementos da interface estão inclusos. Então, crie neste novo arquivo uma classe com os métodos padrão, que já são conhecidos, para todos os elementos da interface. Nesta classe, além do ponteiro do formulário, nós precisaremos do ponteiro para o elemento onde a dica de contexto será ligada e de um método para armazenar esse ponteiro.
//+------------------------------------------------------------------+ //| Tooltip.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" //+------------------------------------------------------------------+ //| Classe para a criação de uma dica de contexto | //+------------------------------------------------------------------+ class CTooltip : public CElement { private: //--- Ponteiro para o formulário ao qual o elemento está anexado CWindow *m_wnd; //--- Ponteiro para o elemento ao qual a dica de contexto está ligada CElement *m_element; //--- public: //--- (1) Armazena o ponteiro do formulário, (2) armazena o ponteiro do elemento void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } void ElementPointer(CElement &object) { m_element=::GetPointer(object); } //--- public: //--- Manipulador de eventos do gráfico virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Move o elemento virtual void Moving(const int x,const int y); //--- (1) Exibe, (2) oculta, (3) reseta, (4) remove virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); }; //+------------------------------------------------------------------+ //| Construtor | //+------------------------------------------------------------------+ CTooltip::CTooltip(void) { } //+------------------------------------------------------------------+ //| Destrutor | //+------------------------------------------------------------------+ CTooltip::~CTooltip(void) { }
Propriedades em que o usuário pode definir antes de criar a dica de contexto:
- Cabeçalho.
- Array de linhas.
Outras propriedades terão os valores predefinidos. Eles são:
- As cores do gradiente do fundo da dica de contexto.
- Cor do quadro de fundo.
- Cor do cabeçalho.
- Cor do texto.
- Canal alfa. Para gerenciar a transparência da dica de contexto.
O array de linha pode ser adicionado linha por linha antes de criar a dica de contexto o método CTooltip::AddString().
class CTooltip : public CElement { private: //--- Propriedades: // Cabeçalho string m_header; //--- Array de linhas do texto da dica de contexto string m_tooltip_lines[]; //--- Valor do canal alfa (transparência da dica de contexto) uchar m_alpha; //--- Cores do (1) texto, (2) do cabeçalho e do (3) fundo do quadro color m_text_color; color m_header_color; color m_border_color; //--- Array do fundo de gradiente color m_array_color[]; //--- public: //--- Cabeçalho da dica de contexto void Header(const string text) { m_header=text; } //--- Adiciona a linha para a dica de contexto void AddString(const string text); }; //+------------------------------------------------------------------+ //| Construtor | //+------------------------------------------------------------------+ CTooltip::CTooltip(void) : m_header(""), m_alpha(0), m_text_color(clrDimGray), m_header_color(C'50,50,50'), m_border_color(C'118,118,118'), m_gradient_top_color(clrWhite), m_gradient_bottom_color(C'208,208,235') { //--- Armazena o nome da classe do elemento na classe base CElement::ClassName(CLASS_NAME); } //+------------------------------------------------------------------+ //| Adiciona a linha | //+------------------------------------------------------------------+ void CTooltip::AddString(const string text) { //--- Aumenta o tamanho do array por um elemento int array_size=::ArraySize(m_tooltip_lines); ::ArrayResize(m_tooltip_lines,array_size+1); //--- Armazenar o valor dos parâmetros passados m_tooltip_lines[array_size]=text; }
Semelhante à classe do elemento linha de separação, nós usaremos a classe CRectCanvas e dois métodos (públicos e privados) para a criação da dica de contexto.
class CTooltip : public CElement { private: //--- Objeto para a criação da dica de contexto CRectCanvas m_canvas; //--- public: //--- Método para criar a dica de contexto bool CreateTooltip (const long chart_id,const int subwin); //--- private: //--- Cria a tela (canvas) para a dica de contexto bool CreateCanvas(void); };
Além da verificação do ponteiro para o formulário onde o elemento irá ser ligado, o método público CTooltip::CreateTooltip() também contém uma verificação do ponteiro para o elemento que foi designado esta dica de contexto. Se o ponteiro do elemento estiver presente, as coordenadas para a dica de contexto são calculadas em relação às coordenadas deste elemento. No nosso caso, este é um pixel abaixo do limite inferior do elemento.
//+------------------------------------------------------------------+ //| Cria o objeto dica de contexto | //+------------------------------------------------------------------+ bool CTooltip::CreateTooltip(const long chart_id,const int subwin) { //--- Retorna se não há nenhum ponteiro do formulário if(::CheckPointer(m_wnd)==POINTER_INVALID) { ::Print(__FUNCTION__," > Antes de criar a dica de contexto, a classe deve ser passada " "para o ponteiro do formulário: CTooltip::WindowPointer(CWindow &object)."); return(false); } //--- Retorna se não há nenhum ponteiro do formulário if(::CheckPointer(m_element)==POINTER_INVALID) { ::Print(__FUNCTION__," > Antes de criar a dica de contexto, a classe deve ser passada " "para o ponteiro do elemento: CTooltip::ElementPointer(CElement &object)."); return(false); } //--- Inicialização dos ponteiros m_id =m_wnd.LastId()+1; m_chart_id =chart_id; m_subwin =subwin; m_x =m_element.X(); m_y =m_element.Y2()+1; //--- Margens da borda CElement::XGap(m_x-m_wnd.X()); CElement::YGap(m_y-m_wnd.Y()); //--- Cria a dica de contexto if(!CreateTooltip()) return(false); //--- return(true); }
Ao contrário do método semelhante na classe CSeparateLine, no método privado CTooltip::CreateCanvas() para a criação da tela de desenho, as seguintes ações são realizadas em vez de desenhar após a criação de um objeto e a definição de suas propriedades.
- Vamos definir o tamanho do array de gradiente para o fundo da dica de contexto. O tamanho do array é igual ao número de pixels da altura do objeto (o tamanho do eixo Y).
- Inicialização do array de gradiente.
- Limpeza da tela. Defina a transparência em 100%. O valor do canal alfa é zero.
No final do método, o objeto é adicionado ao array comum de objetos do elemento. O código deste método é mostrado abaixo.
//+------------------------------------------------------------------+ //| Cria a tela canvas para desenho | //+------------------------------------------------------------------+ bool CTooltip::CreateCanvas(void) { //--- Elaborando o nome do objeto string name=CElement::ProgramName()+"_help_tooltip_"+(string)CElement::Id(); //--- Cria a tela if(!m_canvas.CreateBitmapLabel(m_chart_id,m_subwin,name,m_x,m_y,m_x_size,m_y_size,COLOR_FORMAT_ARGB_NORMALIZE)) return(false); //--- Anexa ao gráfico if(!m_canvas.Attach(m_chart_id,name,m_subwin,1)) return(false); //--- Define as propriedades m_canvas.Background(false); //--- Margens da borda m_canvas.XGap(m_x-m_wnd.X()); m_canvas.YGap(m_y-m_wnd.Y()); //--- Define o tamanho do array de gradiente do fundo da dica de contexto CElement::GradientColorsTotal(m_y_size); ::ArrayResize(m_array_color,m_y_size); //--- Inicialização do array de gradiente CElement::InitColorArray(m_gradient_top_color,m_gradient_bottom_color,m_array_color); //--- Limpeza da tela m_canvas.Erase(::ColorToARGB(clrNONE,0)); m_canvas.Update(); m_alpha=0; //--- Armazena o ponteiro de objeto CElement::AddToArray(m_canvas); return(true); }
Vamos considerar os métodos para desenhar o gradiente vertical e o quadro. Para o gradiente, já existe um array inicializado com os valores necessários no momento de criação da tela. É por isso, que nós precisamos apenas desenhar linha após linha com a cor especificada a partir do array em um loop com o número de iterações igual à altura da tela. Para desenhar o quadro, no início do método, os arrays com as coordenadas de cada lado da tela são declarados e inicializados. Após o quadro ser desenhado, os cantos são arredondados por um pixel e, em seguida, adicionado mais quatro pixels nos quatro cantos internos da tela.
Ambos os métodos possuem apenas um parâmetro - o canal alfa. Isto significa que o grau de transparência do gradiente e do quadro pode ser definido ao chamar esses métodos.
//+------------------------------------------------------------------+ //| Gradiente vertical | //+------------------------------------------------------------------+ void CTooltip::VerticalGradient(const uchar alpha) { //--- Coordenadas X int x1=0; int x2=m_x_size; //--- Desenha o gradiente for(int y=0; y<m_y_size; y++) m_canvas.Line(x1,y,x2,y,::ColorToARGB(m_array_color[y],alpha)); } //+------------------------------------------------------------------+ //| Quadro | //+------------------------------------------------------------------+ void CTooltip::Border(const uchar alpha) { //--- Cor do quadro color clr=m_border_color; //--- Limites int x_size =m_canvas.X_Size()-1; int y_size =m_canvas.Y_Size()-1; //--- Coordenadas: topo/direita/fundo/esquerda int x1[4]; x1[0]=0; x1[1]=x_size; x1[2]=0; x1[3]=0; int y1[4]; y1[0]=0; y1[1]=0; y1[2]=y_size; y1[3]=0; int x2[4]; x2[0]=x_size; x2[1]=x_size; x2[2]=x_size; x2[3]=0; int y2[4]; y2[0]=0; y2[1]=y_size; y2[2]=y_size; y2[3]=y_size; //--- Desenha o quadro pelas coordenadas especificadas for(int i=0; i<4; i++) m_canvas.Line(x1[i],y1[i],x2[i],y2[i],::ColorToARGB(clr,alpha)); //--- Arredonda os cantos por um pixel clr=clrBlack; m_canvas.PixelSet(0,0,::ColorToARGB(clr,0)); m_canvas.PixelSet(0,m_y_size-1,::ColorToARGB(clr,0)); m_canvas.PixelSet(m_x_size-1,0,::ColorToARGB(clr,0)); m_canvas.PixelSet(m_x_size-1,m_y_size-1,::ColorToARGB(clr,0)); //--- Adiciona os pixels através das coordenadas especificadas clr=C'180,180,180'; m_canvas.PixelSet(1,1,::ColorToARGB(clr,alpha)); m_canvas.PixelSet(1,m_y_size-2,::ColorToARGB(clr,alpha)); m_canvas.PixelSet(m_x_size-2,1,::ColorToARGB(clr,alpha)); m_canvas.PixelSet(m_x_size-2,m_y_size-2,::ColorToARGB(clr,alpha)); }
Vamos fazer com que quando o cursor do mouse estiver sobre o elemento, uma dica de contexto irá emergir imediatamente e quando o cursor deixar a área do elemento, a dica de contexto irá desaparecendo gradualmente.
Vamos criar o método CTooltip::showtooltip() para exibir a dica de contexto. No início deste método, haverá uma verificação no valor do campo m_alpha (canal alfa). Se o valor do canal alfa for igual ou maior do que 255, isso significa que o programa já esteve aqui e a dica de contexto está 100% visível por isso não há necessidade de seguir em frente. Caso contrário, o próximo passo é definir as coordenadas, a margem para o cabeçalho da dica de contexto e desenhar o gradiente e o quadro. Caso o usuário não tenha especificado o texto para o cabeçalho, o cabeçalho não será desenhado. Se o texto para o cabeçalho está presente, então os parâmetros de fonte são definidos e o cabeçalho é desenhado.
Após isso, as coordenadas para o texto principal da dica de contexto são definidos tendo em conta a presença do cabeçalho. Em seguida, os parâmetros para o texto principal são definidos. Ao contrário da fonte em negrito do texto do cabeçalho (FW_BLACK), a fonte da principal será menos proeminente (FW_THIN). Em seguida, o texto da dica de contexto é impresso na tela em um loop desde o array inicializado pelo usuário. A coordenada Y é ajustada para cada linha para o valor especificado em cada iteração. No final do processo, a tela é atualizada para exibir as mudanças e há a indicação que a dica de contexto está completamente visível. O código deste método é mostrado abaixo.
//+------------------------------------------------------------------+ //| Exibe a dica de contexto | //+------------------------------------------------------------------+ void CTooltip::ShowTooltip(void) { //--- Sai, se a dica de contexto é 100% visível if(m_alpha>=255) return; //--- Coordenadas e as margens para o cabeçalho int x =5; int y =5; int y_offset =15; //--- Desenha o gradiente VerticalGradient(255); //--- Desenha o quadro Border(255); //--- Desenha o cabeçalho (se especificado) if(m_header!="") { //--- Define os parâmetros da fonte m_canvas.FontSet(FONT,-80,FW_BLACK); //--- Desenha o texto do cabeçalho m_canvas.TextOut(x,y,m_header,::ColorToARGB(m_header_color),TA_LEFT|TA_TOP); } //--- Coordenadas para o texto principal da dica de contexto (considerando a presença do cabeçalho) x=(m_header!="")? 15 : 5; y=(m_header!="")? 25 : 5; //--- Define os parâmetros da fonte m_canvas.FontSet(FONT,-80,FW_THIN); //--- Desenha o texto principal da dica de contexto int lines_total=::ArraySize(m_tooltip_lines); for(int i=0; i<lines_total; i++) { m_canvas.TextOut(x,y,m_tooltip_lines[i],::ColorToARGB(m_text_color),TA_LEFT|TA_TOP); y=y+y_offset; } //--- Atualiza a tela m_canvas.Update(); //--- Indicação de uma dica de contexto completamente visível m_alpha=255; }
Vamos escrever um método para o desvanecimento gradual da dica de contexto e nomeá-la CTooltip::FadeOutTooltip(). No início deste método, é realizado uma verificação de reversão para o valor do canal alfa. Isto significa que, se a transparência completa foi alcançada, não há necessidade de seguir em frente. Se este não for o caso, a tela é redesenhada em um loop com um passo especificado para o desvanecimento até que a transparência completa seja alcançada. Uma versão completa deste método é mostrado no código abaixo.
//+------------------------------------------------------------------+ //| Desvanecimento gradual da dia de contexto | //+------------------------------------------------------------------+ void CTooltip::FadeOutTooltip(void) { //--- Sai, se a dica de contexto está 100% oculta if(m_alpha<1) return; //--- Margem para o cabeçalho int y_offset=15; //--- Passo da transparência uchar fadeout_step=7; //--- Desvanecimento gradual da dica de contexto for(uchar a=m_alpha; a>=0; a-=fadeout_step) { //--- Se o próximo passo torna-a negativa, pare o loop if(a-fadeout_step<0) { a=0; m_canvas.Erase(::ColorToARGB(clrNONE,0)); m_canvas.Update(); m_alpha=0; break; } //--- Coordenadas para o cabeçalho int x =5; int y =5; //--- Desenha o gradiente e o quadro VerticalGradient(a); Border(a); //--- Desenha o cabeçalho (se especificado) if(m_header!="") { //--- Define os parâmetros da fonte m_canvas.FontSet(FONT,-80,FW_BLACK); //--- Desenha o texto do cabeçalho m_canvas.TextOut(x,y,m_header,::ColorToARGB(m_header_color,a),TA_LEFT|TA_TOP); } //--- Coordenadas para o texto principal da dica de contexto (considerando a presença do cabeçalho) x=(m_header!="")? 15 : 5; y=(m_header!="")? 25 : 5; //--- Define os parâmetros da fonte m_canvas.FontSet(FONT,-80,FW_THIN); //--- Desenha o texto principal da dica de contexto int lines_total=::ArraySize(m_tooltip_lines); for(int i=0; i<lines_total; i++) { m_canvas.TextOut(x,y,m_tooltip_lines[i],::ColorToARGB(m_text_color,a),TA_LEFT|TA_TOP); y=y+y_offset; } //--- Atualiza a tela m_canvas.Update(); } }
Agora, nós temos todos os métodos necessários para a criação e o desenho da dica de contexto. Eles podem ser chamados no manipulador de evento do elemento. Para descobrir se o botão para exibir as dicas de contexto foi pressionado no formulário, crie o método >CWindow::TooltipBmpState() na classe CWindow.
class CWindow : public CElement { public: //--- Verifica o modo de exibição da dica de contexto bool TooltipBmpState(void) const { return(m_button_tooltip.State()); } };
Agora, se o foco está sobre o elemento, a dica de contexto será exibida desde que o modo de exibição da mesma esteja habilitado. Se o cursor do mouse estiver fora da área do elemento ou o formulário está bloqueado, então a dica de contexto será ocultada.
//+------------------------------------------------------------------+ //| Manipulador de eventos do gráfico | //+------------------------------------------------------------------+ void CTooltip::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Manipulação do evento do movimento do cursor if(id==CHARTEVENT_MOUSE_MOVE) { //--- Sai se o elemento está oculto if(!CElement::IsVisible()) return; //--- Sai, se o botão dica de contexto estiver desativado if(!m_wnd.TooltipBmpState()) return; //--- Se o formulário está bloqueado if(m_wnd.IsLocked()) { //--- Oculta a dica de contexto FadeOutTooltip(); return; } //--- Se o foco está sobre o elemento if(m_element.MouseFocus()) //--- Exibe a dica de contexto ShowTooltip(); //--- Se não houver foco else //--- Oculta a dica de contexto FadeOutTooltip(); //--- return; } }
Teste da Dica de Contexto
Agora, nós podemos testar como tudo está funcionando. Atualmente, existem cinco botões no formulário do EA de teste. Vamos criar uma dica de contexto para cada botão. Declare cinco instâncias da classe do tipo CTooltip e cinco métodos. Nós vamos mostrar a implementação para apenas um deles como um exemplo.
class CProgram : public CWndEvents { private: CTooltip m_tooltip1; CTooltip m_tooltip2; CTooltip m_tooltip3; CTooltip m_tooltip4; CTooltip m_tooltip5; //--- private: bool CreateTooltip1(void); bool CreateTooltip2(void); bool CreateTooltip3(void); bool CreateTooltip4(void); bool CreateTooltip5(void); }; //+------------------------------------------------------------------+ //| Cria a dica de contexto 5 | //+------------------------------------------------------------------+ bool CProgram::CreateTooltip5(void) { #define TOOLTIP5_LINES_TOTAL 3 //--- Armazena o ponteiro da janela m_tooltip5.WindowPointer(m_window1); //--- Armazenar o ponteiro do elemento m_tooltip5.ElementPointer(m_icon_button5); //--- Array com o texto dica de contexto string text[]= { "Controle \"Botão com ícone\" (5).", "Esta é a segunda linha da dica de contexto.", "Esta é a terceira linha da dica de contexto." }; //--- Define as propriedades antes da criação m_tooltip5.Header("Icon Button 5"); m_tooltip5.XSize(250); m_tooltip5.YSize(80); //--- Adiciona o texto linha por linha for(int i=0; i<TOOLTIP5_LINES_TOTAL; i++) m_tooltip5.AddString(text[i]); //--- Cria um controle if(!m_tooltip5.CreateTooltip(m_chart_id,m_subwin)) return(false); //--- Adiciona um objeto ao array comum de grupos de objetos CWndContainer::AddToElementsArray(0,m_tooltip5); return(true); }
A criação das dicas de contexto devem ser conduzidas por último no método de criação da interface gráfica de modo que elas se encontram no final do array de elemento na base. Desta forma, as dicas de contexto estarão sempre acima de outros objetos no gráfico.
//+------------------------------------------------------------------+ //| Cria o painel de negociação | //+------------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- Criação do formulário 1 para os controles //--- Criação dos controles: // Menu principal //--- Menus de contexto //--- Criando a barra de status //--- Botões com Ícone //--- Dicas de contexto if(!CreateTooltip1()) return(false); if(!CreateTooltip2()) return(false); if(!CreateTooltip3()) return(false); if(!CreateTooltip4()) return(false); if(!CreateTooltip5()) return(false); //--- Redesenho do gráfico m_chart.Redraw(); return(true); }
Para exibir o botão dica de contexto sobre o formulário, utilize o método CWindow::UseTooltipsButton() no momento de sua criação.
//+------------------------------------------------------------------+ //| Cria o formulário 1 para os controles | //+------------------------------------------------------------------+ bool CProgram::CreateWindow1(const string caption_text) { //--- Adiciona o ponteiro da janela para o array de janela CWndContainer::AddWindow(m_window1); //--- Coordenadas int x=1; int y=20; //--- Propriedades m_window1.Movable(true); m_window1.XSize(251); m_window1.YSize(200); m_window1.UseTooltipsButton(); m_window1.CaptionBgColor(clrCornflowerBlue); m_window1.CaptionBgColorHover(C'150,190,240'); //--- Cria o formulário if(!m_window1.CreateWindow(m_chart_id,m_subwin,caption_text,x,y)) return(false); //--- return(true); }
Você deve obter o resultado como é mostrado na imagem abaixo.
Fig. 4. Teste da dica de contexto.
Ele tem um boa aparência e parece estar funcionando bem! Agora, este elemento requer um array privado no ponteiro base. Quando estivermos desenvolvendo o modo multi-janela, será ilustrado quais casos nós podemos precisar de tal array.
Array Privado para a Dica de Contexto
Então, vá para a classe CWndContainer e melhore-o com os novos membros. Um array privado para as dicas de contexto é para ser adicionado à estrutura WindowElements. Nós também vamos criar (1) o método CWndContainer::TooltipsTotal() para a obtenção da quantidade de dicas de contexto e (2) o método CWndContainer::AddTooltipElements() para adicionar o ponteiro da dica de contexto ao array privado.
class CWndContainer { protected: //--- Estrutura de arrays de elementos struct WindowElements { //--- Dicas de contexto CTooltip *m_tooltips[]; }; //--- Array de arrays de elementos para cada janela WindowElements m_wnd[]; //--- public: //--- Quantidade das dicas de contexto int TooltipsTotal(const int window_index); //--- private: //--- Armazena os ponteiros aos elementos dicas de contexto na base bool AddTooltipElements(const int window_index,CElement &object); }; //+------------------------------------------------------------------+ //| Retorna a quantidade de dicas de contexto pelo índice da janela especificada| //+------------------------------------------------------------------+ int CWndContainer::TooltipsTotal(const int window_index) { if(window_index>=::ArraySize(m_wnd)) { ::Print(PREVENTING_OUT_OF_RANGE); return(WRONG_VALUE); } //--- return(::ArraySize(m_wnd[window_index].m_tooltips)); } //+------------------------------------------------------------------+ //| Armazena o ponteiro dica de contexto no array privado | //+------------------------------------------------------------------+ bool CWndContainer::AddTooltipElements(const int window_index,CElement &object) { //--- Retorna, se este não for uma dica de contexto if(object.ClassName()!="CTooltip") return(false); //--- Obtém o ponteiro dica de contexto CTooltip *t=::GetPointer(object); //--- Adiciona o ponteiro para o array privado AddToRefArray(t,m_wnd[window_index].m_tooltips); return(true); }
o método CWndContainer::AddTooltipElements() deve ser chamado no método público CWndContainer::AddToElementsArray(), que será utilizado na classe personalizada quando se adiciona o elemento para a base. A sua versão reduzida é apresentada no código abaixo.
//+------------------------------------------------------------------+ //| Adiciona o ponteiro ao array de elemento | //+------------------------------------------------------------------+ void CWndContainer::AddToElementsArray(const int window_index,CElement &object) { //--- Se a base não contém formulários para os controles / --- Se a solicitação for para um formulário que não existe //--- Adiciona ao array comum de elementos //--- Adiciona os objetos do elemento para o array comum de objetos //--- Armazena o ID do último elemento em todos os formulários //--- Aumenta o contador de identificadores de elemento //--- Armazena os ponteiros para os objetos do menu de contexto na base //--- Armazena os ponteiros para os objetos do menu principal na base //--- Armazena os ponteiros aos elementos do botão de divisão na base //--- Armazena os ponteiros aos elementos dicas de contexto na base if(AddTooltipElements(window_index,object)) return; }
O desenvolvimento da classe para a criação das dicas de contexto está finalizado. Você pode baixar a versão completa nos arquivos anexados deste artigo.
Conclusão
Neste artigo, nós discutimos o desenvolvimento da barra de status e a dica de contexto da interface informativa. No próximo capítulo, nós vamos implementar a possibilidade de criar interfaces gráficas de multi-janelas e discutir o sistema de gestão de prioridades do botão esquerdo do mouse.
Você pode encontrar e baixar o material da primeira parte ou toda a série nos arquivos anexados, assim, você pode testar o seu funcionamento. Se você tiver dúvidas sobre a utilização do material a partir desses arquivos, você poderá consultar a descrição detalhada do desenvolvimento da biblioteca em um dos artigos da lista abaixo ou fazer sua pergunta nos comentários deste artigo.
Lista de artigos (capítulos) da quarta parte:
- Interfaces Gráficas IV: Elementos Informativos da Interface (Capítulo 1)
- Interfaces gráficas IV: O Modo Multi-Janela e o Sistema de Prioridades (Capítulo 2)
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2307
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso