Interfaces Gráficas VI: Os Controles Deslizante e Deslizante Duplo (Capítulo 2)
Conteúdo
- Introdução
- O Controle Deslizante
- O Controle Deslizante Duplo
- Desenvolvimento de uma Classe para a Criação do Controle Deslizante Duplo
- Teste do Controle Deslizante Duplo
- Conclusão
Introdução
O primeiro artigo Interfaces gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1) explica em detalhes a finalidade desta biblioteca. Você irã encontrar uma lista de artigos com os links no final de cada capítulo. 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 artigo anterior, nós enriquecemos a nossa biblioteca com quatro controles que são frequentemente usados nas interfaces gráficas: caixa de seleção, campo de edição, campo de edição com caixa de seleção e a lista combinada com a caixa de seleção. O segundo capítulo da sexta parte será dedicado aos controles deslizante e deslizante duplo.
O Controle Deslizante
O Controle deslizante é um tipo do controle campo de edição que contém um intervalo limitado por um valor mínimo e máximo. Ao contrário do controle campo de edição que nós discutimos em detalhes no artigo anterior, o controle deslizante não possui botões para alterar o valor no campo de entrada. Para esse propósito, existe uma linha ao longo da qual o cursor pode ser deslocado. Tal elemento de interface é adequado para os casos em que é necessário apenas um valor aproximado dentro de um certo intervalo, não sendo necessário seu valor preciso. No entanto, ainda há a possibilidade de introduzir um valor preciso.
Este elemento será composto por seis objetos gráficos. Eles são:
- Fundo
- Título (rótulo de texto)
- Campo de entrada
- Linha do controle deslizante.
- Corrediça do controle deslizante
- Indicador do controle deslizante
Fig. 1. Partes integrantes do controle deslizante.
Vamos estudar a classe desse controle em detalhes.
Desenvolvimento de uma Classe para a Criação do Controle Deslizante
Crie o arquivo Slider.mqh e inclua-o no arquivo WndContainer.mqh:
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Slider.mqh"
No arquivo Slider.mqh crie a classe CSlider com o conjunto padrão de métodos que devem estar presentes em cada controle da biblioteca em desenvolvimento. Ao lado dos arquivos de inclusão Element.mqh e Window.mqh, inclua o arquivo SeparateLine.mqh com a classe CSeparateLine para a criação de uma linha de separação. A classe CSeparateLine já foi discutida no artigo Interfaces Gráficas II: Os Elementos Linha de Separação e o Menu de Contexto (Capítulo 2)e, portanto, não vamos considerá-la novamente. A única coisa que eu gostaria de lembrá-lo é que se a altura definida for maior do que dois pixels, haverá um espaço vazio entre as duas linhas desenhadas. Visualmente, ela se parece como um buraco que é adequado para a criação de uma linha do controle deslizante (fenda ou ranhura), onde a corrediça do controle deslizante estará se movendo.
//+------------------------------------------------------------------+ //| Slider.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "SeparateLine.mqh" //+------------------------------------------------------------------+ //| Classe para criar o controle deslizante com o campo de edição | //+------------------------------------------------------------------+ class CSlider : public CElement { private: //--- Ponteiro para o formulário ao qual o elemento está anexado CWindow *m_wnd; public: CSlider(void); ~CSlider(void); //--- public: //--- 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); //--- Reseta a cor virtual void ResetColors(void); };
A biblioteca do usuário deve ter a possibilidade de definir as propriedades de todos os objetos que o controle deslizante será composto. Estas propriedades são listadas abaixo.
- Cor de fundo do elemento
- Texto para descrever o controle deslizante
- Cores do rótulo de texto em diferentes estados
- Valor atual do campo de entrada
- Tamanho do campo de entrada
- Cores do campo de entrada em diferentes estados
- Cores do texto do campo de entrada em diferentes estados
- Cores do quadro do campo de entrada em diferentes estados
- Tamanho da fenda ao longo do eixo Y (altura)
- Cores das linhas da fenda
- Cores do indicador do controle deslizante em diferentes estados
- Tamanho da corrediça do controle deslizante
- Cores da corrediça do controle deslizante
- Prioridades do clique do botão esquerdo do mouse
Abaixo está o código dos campos e métodos da classe das propriedades dos objetos listados acima:
class CSlider : public CElement { private: //--- Cor de fundo do elemento color m_area_color; //--- Texto para descrever o controle deslizante string m_label_text; //--- Cores do rótulo de texto em diferentes estados color m_label_color; color m_label_color_hover; color m_label_color_locked; color m_label_color_array[]; //--- Tamanho do campo de entrada int m_edit_x_size; int m_edit_y_size; //--- Cores do campo de entrada em diferentes estados color m_edit_color; color m_edit_color_locked; //--- Cores do texto do campo de entrada em diferentes estados color m_edit_text_color; color m_edit_text_color_locked; //--- Cores da estrutura do campo de entrada em diferentes estados color m_edit_border_color; color m_edit_border_color_hover; color m_edit_border_color_locked; color m_edit_border_color_array[]; //--- Tamanho da fenda int m_slot_y_size; //--- Cores da fenda color m_slot_line_dark_color; color m_slot_line_light_color; //--- Cores do indicador em diferentes estados color m_slot_indicator_color; color m_slot_indicator_color_locked; //--- Tamanho da corrediça do controle deslizante int m_thumb_x_size; int m_thumb_y_size; //--- Cores da corrediça do controle deslizante color m_thumb_color; color m_thumb_color_hover; color m_thumb_color_locked; color m_thumb_color_pressed; //--- Prioridades do clique do botão esquerdo do mouse int m_zorder; int m_area_zorder; int m_edit_zorder; //--- public: //--- (1) cor de fundo, (2) cores do rótulo de texto void AreaColor(const color clr) { m_area_color=clr; } void LabelColor(const color clr) { m_label_color=clr; } void LabelColorHover(const color clr) { m_label_color_hover=clr; } void LabelColorLocked(const color clr) { m_label_color_locked=clr; } //--- Tamanho do (1) campo de entrada e (2) a fenda void EditXSize(const int x_size) { m_edit_x_size=x_size; } void EditYSize(const int y_size) { m_edit_y_size=y_size; } void SlotYSize(const int y_size) { m_slot_y_size=y_size; } //--- Cores do campo de entrada em diferentes estados void EditColor(const color clr) { m_edit_color=clr; } void EditColorLocked(const color clr) { m_edit_color_locked=clr; } //--- Cores do texto do campo de entrada em diferentes estados void EditTextColor(const color clr) { m_edit_text_color=clr; } void EditTextColorLocked(const color clr) { m_edit_text_color_locked=clr; } //--- Cores da estrutura do campo de entrada em diferentes estados void EditBorderColor(const color clr) { m_edit_border_color=clr; } void EditBorderColorHover(const color clr) { m_edit_border_color_hover=clr; } void EditBorderColorLocked(const color clr) { m_edit_border_color_locked=clr; } //--- Linha de separação na cor (1) escura e (2) clara void SlotLineDarkColor(const color clr) { m_slot_line_dark_color=clr; } void SlotLineLightColor(const color clr) { m_slot_line_light_color=clr; } //--- Cores do indicador deslizante em diferentes estados void SlotIndicatorColor(const color clr) { m_slot_indicator_color=clr; } void SlotIndicatorColorLocked(const color clr) { m_slot_indicator_color_locked=clr; } //--- Tamanho da corrediça do controle deslizante void ThumbXSize(const int x_size) { m_thumb_x_size=x_size; } void ThumbYSize(const int y_size) { m_thumb_y_size=y_size; } //--- Cores da corrediça do controle deslizante void ThumbColor(const color clr) { m_thumb_color=clr; } void ThumbColorHover(const color clr) { m_thumb_color_hover=clr; } void ThumbColorLocked(const color clr) { m_thumb_color_locked=clr; } void ThumbColorPressed(const color clr) { m_thumb_color_pressed=clr; } };
As propriedades da lista anterior se relacionam principalmente com a cor e o tamanho dos objetos do controle. As propriedades relacionadas com o alcance e o campo de entrada do cursor será mostrado em um grupo separado. Estas propriedades são:
- Valor mínimo
- Valor máximo
- Passo da variação dos valores no campo de entrada
- Modo de alinhamento de texto
- Número de casas decimais
class CSlider : public CElement { private: //--- (1) Valor mínimo (2) e máximo, (3) passo para alterar o valor double m_min_value; double m_max_value; double m_step_value; //--- Número de casas decimais int m_digits; //--- Modo de alinhamento de texto ENUM_ALIGN_MODE m_align_mode; //--- public: //--- Valor mínimo double MinValue(void) const { return(m_min_value); } void MinValue(const double value) { m_min_value=value; } //--- Valor máximo double MaxValue(void) const { return(m_max_value); } void MaxValue(const double value) { m_max_value=value; } //--- Passo de alteração do valor double StepValue(void) const { return(m_step_value); } void StepValue(const double value) { m_step_value=(value<=0)? 1 : value; } //--- (1) Número de casas decimais, (2) o modo de alinhamento do texto void SetDigits(const int digits) { m_digits=::fabs(digits); } void AlignMode(ENUM_ALIGN_MODE mode) { m_align_mode=mode; } };
Para obter o valor atual, bem como para ajustar e definir o novo valor no campo de entrada, nós vamos usar os métodos CSlider::GetValue(), CSlider::SetValue() e o CSlider::ChangeValue():
class CSlider : public CElement { private: //--- Valor atual do campo de entrada double m_edit_value; //--- public: //--- Retorna e define o valor do campo de entrada double GetValue(void) const { return(m_edit_value); } bool SetValue(const double value); //--- Valor atual do campo de entrada void ChangeValue(const double value); }; //+------------------------------------------------------------------+ //| Define o valor atual | //+------------------------------------------------------------------+ bool CSlider::SetValue(const double value) { //--- Para o ajuste double corrected_value=0.0; //--- Ajustar considerando o passo corrected_value=::MathRound(value/m_step_value)*m_step_value; //--- Verifica a mínima/máxima if(corrected_value<=m_min_value) corrected_value=m_min_value; if(corrected_value>=m_max_value) corrected_value=m_max_value; //--- Se o valor tiver sido alterado if(m_edit_value!=corrected_value) { m_edit_value=corrected_value; return(true); } //--- Valor inalterado return(false); } //+------------------------------------------------------------------+ //| Valor atual do campo de entrada | //+------------------------------------------------------------------+ void CSlider::ChangeValue(const double value) { //--- Verifica, ajusta e armazena o novo valor SetValue(value); //--- Define o novo valor no campo de entrada m_edit.Description(::DoubleToString(GetValue(),m_digits)); }
Quando a corrediça do controle deslizante se move, o valor no campo de entrada deve ser calculado em relação a coordenada X. Se o valor for inserido manualmente, então a coordenada X da corrediça do controle deslizante deve ser calculada em relação ao novo valor no campo de entrada. Em outras palavras, a possibilidade da transformação inversa deve ser permitida quando o elemento for desenvolvido.
Para o cálculo correto, nós precisaremos de campos auxiliares (variáveis) da classe que será utilizada no cálculo. Estas variáveis deverão ser calculadas (inicializadas) apenas uma vez, imediatamente após o elemento ter sido criado. Abaixo está a descrição dessas variáveis.
- Número de pixels na área de trabalho (m_pixels_total).
- Número de passos no intervalo de valores da área de trabalho (m_value_steps_total).
- Passo em relação à largura da área de trabalho (m_position_step).
Vamos escrever um método para calcular esses valores e chamá-lo de CSlider::CalculateCoefficients():
class CSlider : public CElement { private: //--- Número de pixels na área de trabalho int m_pixels_total; //--- Número de passos na área de trabalho int m_value_steps_total; //--- Passo da largura transformada da área de trabalho double m_position_step; //--- private: //--- Cálculo dos valores (passos e coeficientes) bool CalculateCoefficients(void); }; //+------------------------------------------------------------------+ //| Cálculo dos valores (passos e coeficientes) | //+------------------------------------------------------------------+ bool CSlider::CalculateCoefficients(void) { //--- Sai se a largura do elemento for menor do que a largura da corrediça do controle deslizante if(CElement::XSize()<m_thumb_x_size) return(false); //--- Número de pixels na área de trabalho m_pixels_total=CElement::XSize()-m_thumb_x_size; //--- Número de passos no valor do intervalo da área de trabalho m_value_steps_total=int((m_max_value-m_min_value)/m_step_value); //--- Passo em relação à largura da área de trabalho m_position_step=m_step_value*(double(m_value_steps_total)/double(m_pixels_total)); return(true); }
Agora, os valores das variáveis listadas acima podem ser utilizadas para o cálculo da coordenada X da corrediça do controle deslizante em relação ao valor no campo de entrada e vice-versa. Para isso, nós vamos escrever dois métodos distintos, o CSlider::CalculateThumbX() e o CSlider::CalculateThumbPos().
No início do método CSlider::CalculateThumbX(), nós iremos calcular os valores da variável local auxiliar (neg_range) para ajuste no caso do valor do limite mínimo ser negativo. Então, nós vamos calcular a coordenada X para a corrediça do controle deslizante. Depois disso, no caso da linha do controle deslizante exceder, o valor é ajustado. No final do método, é definido um novo valor da coordenada X para a corrediça do controle deslizante e a margem da borda do formulário que o elemento está anexado é calculada de novo.
No início do método CSlider::CalculateThumbPos(), nós obtemos a posição da corrediça do controle deslizante na faixa de valores. Em seguida, é realizado um ajuste se o valor do limite mínimo for negativo e o valor da variável m_current_pos_x estiver correta. Então, se a área de trabalho foi excedida, é feito uma alteração no valor.
class CSlider : public CElement { private: //--- Posição atual da corrediça do controle deslizante: (1) valor, (2) a coordenada X double m_current_pos; double m_current_pos_x; //--- private: //--- Cálculo da coordenada X da corrediça do controle deslizante void CalculateThumbX(void); //--- Altera a posição atual da corrediça do controle deslizante em relação ao valor atual void CalculateThumbPos(void); }; //+------------------------------------------------------------------+ //| Cálculo da coordenada X do deslizado do cursor | //+------------------------------------------------------------------+ void CSlider::CalculateThumbX(void) { //--- Ajuste considerando que o valor mínimo pode ser negativo double neg_range=(m_min_value<0)? ::fabs(m_min_value/m_position_step) : 0; //--- Calcula a coordenada X para a corrediça do controle deslizante m_current_pos_x=m_area.X()+(m_edit_value/m_position_step)+neg_range; //--- Se a área de trabalho exceder pelo lado esquerdo if(m_current_pos_x<m_area.X()) m_current_pos_x=m_area.X(); //--- Se a área de trabalho exceder pelo lado direito if(m_current_pos_x+m_thumb.XSize()>m_area.X2()) m_current_pos_x=m_area.X2()-m_thumb.XSize(); //--- Armazena e define a nova coordenada X m_thumb.X(int(m_current_pos_x)); m_thumb.X_Distance(int(m_current_pos_x)); m_thumb.XGap(m_thumb.X()-m_wnd.X()); } //+------------------------------------------------------------------+ //| Cálculo da posição da corrediça do controle deslizante na faixa de valores | //+------------------------------------------------------------------+ void CSlider::CalculateThumbPos(void) { //--- Obtém o número da posição da corrediça do controle deslizante m_current_pos=(m_thumb.X()-m_area.X())*m_position_step; //--- Ajuste considerando que o valor mínimo pode ser negativo if(m_min_value<0 && m_current_pos_x!=WRONG_VALUE) m_current_pos+=int(m_min_value); //--- Verifica se a área de trabalho excedeu pela direita/esquerda if(m_thumb.X2()>=m_area.X2()) m_current_pos=int(m_max_value); if(m_thumb.X()<=m_area.X()) m_current_pos=int(m_min_value); }
Quando os deslizador do cursor está se movendo, a largura do indicador do controle deslizante deve ser calculado e atualizado. O lado direito do indicador do controle deslizante deve ser vinculado a corrediça do controle deslizante Para isso, vamos escrever o método CSlider::UpdateIndicator():
class CSlider : public CElement { private: //--- Atualiza o indicador do controle deslizante void UpdateIndicator(void); }; //+------------------------------------------------------------------+ //| Atualiza o indicador do controle deslizante | //+------------------------------------------------------------------+ void CSlider::UpdateIndicator(void) { //--- Calcula o tamanho int x_size=m_thumb.X()-m_indicator.X(); //--- Ajuste no caso dos valores não serem permitidos if(x_size<=0) x_size=1; //--- Define um novo tamanho m_indicator.X_Size(x_size); }
Para criar o controle deslizante nós precisaremos de seis métodos privados e um público.
class CSlider : public CElement { public: //--- Métodos para criar o controle bool CreateSlider(const long chart_id,const int subwin,const string text,const int x,const int y); //--- private: bool CreateArea(void); bool CreateLabel(void); bool CreateEdit(void); bool CreateSlot(void); bool CreateIndicator(void); bool CreateThumb(void); };
Nós vamos discutir apenas o código do método CSlider::CreateThumb(), porque este é o lugar onde é chamado todos os métodos de cálculos considerados anteriormente. Outros métodos não possuem nada que nós já não estudamos nos artigos anteriores desta série.
//+------------------------------------------------------------------+ //| Cria a corrediça do controle deslizante | //+------------------------------------------------------------------+ bool CSlider::CreateThumb(void) { //--- Elaborando o nome do objeto string name=CElement::ProgramName()+"_slider_thumb_"+(string)CElement::Id(); //--- Coordenadas int x=CElement::X(); int y=m_slot.Y()-((m_thumb_y_size-m_slot_y_size)/2); //--- Define o objeto if(!m_thumb.Create(m_chart_id,name,m_subwin,x,y,m_thumb_x_size,m_thumb_y_size)) return(false); //--- Define as propriedades m_thumb.Color(m_thumb_color); m_thumb.BackColor(m_thumb_color); m_thumb.BorderType(BORDER_FLAT); m_thumb.Corner(m_corner); m_thumb.Selectable(false); m_thumb.Z_Order(m_zorder); m_thumb.Tooltip("\n"); //--- Armazenar o tamanho (no objecto) m_thumb.XSize(m_thumb.X_Size()); m_thumb.YSize(m_thumb.Y_Size()); //--- Armazena as coordenadas m_thumb.X(x); m_thumb.Y(y); //--- Margens da borda m_thumb.XGap(x-m_wnd.X()); m_thumb.YGap(y-m_wnd.Y()); //--- Calcula os valores das variáveis auxiliares CalculateCoefficients(); //--- Calcula as coordenadas X da corrediça do controle deslizante em relação ao valor atual no campo de entrada CalculateThumbX(); //--- Cálculo da posição da corrediça do controle deslizante na faixa do valor CalculateThumbPos(); //--- Atualiza o indicador do controle deslizante UpdateIndicator(); //--- Armazena o ponteiro de objeto CElement::AddToArray(m_thumb); return(true); }
Os métodos de mover a corrediça do controle deslizante são idênticos aos métodos com nomes semelhantes nas classes CScroll e CScrollH, que foram discutidos em detalhe no artigo Interfaces Gráficas V: A Barra de Rolagem Vertical e Horizontal (Capítulo 1). É por isso que nós não vamos considerar o seu código aqui. Nós só vamos declará-los no corpo da classe CSlider.
class CSlider : public CElement { private: //--- Estado do botão do mouse (pressionado/liberado) ENUM_THUMB_MOUSE_STATE m_clamping_area_mouse; //--- Para identificar o modo do movimento da corrediça do controle deslizante bool m_slider_thumb_state; //--- Variáveis relacionadas com o movimento da corrediça do controle deslizante int m_slider_size_fixing; int m_slider_point_fixing; //--- private: //--- Processo do movimento deslizante do cursor void OnDragThumb(const int x); //--- Atualização da localização da corrediça do controle deslizante void UpdateThumb(const int new_x_point); //--- Verifica o estado do botão do mouse void CheckMouseButtonState(void); //--- Zerando as variáveis relacionadas com o movimento deslizante do cursor void ZeroThumbVariables(void); };
Para lidar com os valores inseridos no campo de entrada, crie o método CSlider::OnEndEdit() que será chamado no manipulador de eventos CSlider::OnEvent().
No início do método CSlider::OnEndEdit(), há uma verificação com o nome do objeto se o valor foi inserido no campo deste cursor. Em seguida, obtenha o valor atual do campo de entrada. Então, siga uma verificação obrigatória para a entrada com valores não permitidos, ajuste, o cálculo da coordenada X da corrediça do controle deslizante e a posição na faixa do valor. Depois disso, o indicador do controle deslizante é atualizado. No final deste método, uma mensagem deve ser enviada com (1) o identificador de evento personalizado ON_END_EDIT, (2) o identificador do elemento, (3) o índice do elemento e (4) a descrição contida no rótulo de texto.
class CSlider : public CElement { private: //--- Lidando com a inserção do valor no campo de entrada bool OnEndEdit(const string object_name); }; //+------------------------------------------------------------------+ //| Lidando com a inserção do valor no campo de entrada | //+------------------------------------------------------------------+ bool CSlider::OnEndEdit(const string object_name) { //--- Sai, se o nome do objeto for diferente if(object_name!=m_edit.Name()) return(false); //--- Obter o valor inserido double entered_value=::StringToDouble(m_edit.Description()); //--- Verifica, ajusta e armazena o novo valor ChangeValue(entered_value); //--- Calcula a coordenada X da corrediça do controle deslizante CalculateThumbX(); //--- Calcula a posição no intervalo de valores CalculateThumbPos(); //--- Atualiza o indicador do controle deslizante UpdateIndicator(); //--- Envia uma mensagem sobre ele ::EventChartCustom(m_chart_id,ON_END_EDIT,CElement::Id(),CElement::Index(),m_label.Description()); return(true); }
A mesma mensagem personalizada deve ser enviada após a corrediça do controle deslizante parar de se mover para alterar o valor do campo de entrada. O método CSlider::ZeroThumbVariables() serve muito bem para este objetivo. Isso é chamado no método CSlider::CheckMouseButtonState(), onde a área do clique do botão esquerdo do mouse é monitorada. A chamada do método CSlider::ZeroThumbVariables() já implica que o botão esquerdo do mouse já foi liberado. Se ele é pressionado para baixo sobre a área da corrediça do controle deslizante, significa que o movimento dele foi finalizado e uma mensagem deverá ser enviada dizendo que o valor no campo de entrada foi alterado.
//+------------------------------------------------------------------+ //| Zerando as variáveis relacionadas ao movimento da barra de rolagem | //+------------------------------------------------------------------+ void CSlider::ZeroThumbVariables(void) { //--- Se você está aqui, isso significa que o botão esquerdo do mouse foi liberado. // Se o botão esquerdo do mouse foi pressionado sobre o segundo controle deslizante... if(m_clamping_area_mouse==THUMB_PRESSED_INSIDE) { //--- ...envia uma mensagem que a alteração do valor no campo de entrada com a corrediça do controle deslizante foi concluída ::EventChartCustom(m_chart_id,ON_END_EDIT,CElement::Id(),CElement::Index(),m_label.Description()); } //--- m_slider_size_fixing =0; m_clamping_area_mouse =THUMB_NOT_PRESSED; //--- Se o identificador do elemento coincide com o identificador de ativação, // desbloqueia o formulário e redefine o identificador do elemento ativado if(CElement::Id()==m_wnd.IdActivatedElement()) { m_wnd.IsLocked(false); m_wnd.IdActivatedElement(WRONG_VALUE); } }
O código completo do manipulador de evento CSlider::OnEvent(), neste caso, ficará da seguinte forma:
//+------------------------------------------------------------------+ //| Manipulador de eventos do gráfico | //+------------------------------------------------------------------+ void CSlider::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; //--- Coordenadas e o estado do botão esquerdo do mouse int x=(int)lparam; int y=(int)dparam; m_mouse_state=(bool)int(sparam); //--- Verificando o foco sobre os elementos CElement::MouseFocus(x>X() && x<X2() && y>Y() && y<Y2()); m_thumb.MouseFocus(x>m_thumb.X() && x<m_thumb.X2() && y>m_thumb.Y() && y<m_thumb.Y2()); //--- Sai, se o elemento está bloqueado if(!m_slider_state) return; //--- Verifica e armazena o estado do botão do mouse CheckMouseButtonState(); //--- Altera a cor da corrediça do controle deslizante ChangeThumbColor(); //--- Se a gestão foi passada para a linha do controle deslizante, identifica a sua localização if(m_clamping_area_mouse==THUMB_PRESSED_INSIDE) { //--- Movendo a corrediça do controle deslizante OnDragThumb(x); //--- Cálculo da posição da corrediça do controle deslizante na faixa do valor CalculateThumbPos(); //--- Define o novo valor no campo de entrada ChangeValue(m_current_pos); //--- Atualiza o indicador do controle deslizante UpdateIndicator(); return; } } //--- Lidando com a alteração do valor no evento campo de entrada if(id==CHARTEVENT_OBJECT_ENDEDIT) { //--- Manipula o valor de entrada if(OnEndEdit(sparam)) return; } }
Nós implementamos os métodos para a criação e gerenciamento do controle deslizante. Vamos testá-lo no programa em MQL que nós utilizamos nos artigos anteriores.
Teste do Controle Deslizante
No artigo anterior, na aplicação de teste, nós criamos quatro caixas de seleção que gerenciavam a disponibilidade de outros elementos. Nós vamos adicionar o controle deslizante e a quinta caixa de seleção para gerir a sua disponibilidade na interface gráfica. Para isso, na classe personalizada CProgram, declare as instâncias da classe CCheckBox e CSlider, bem como os métodos com margens a partir da borda do formulário que estes controles serão vinculados.
//+------------------------------------------------------------------+ //| Classe para a criação de um aplicativo | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { private: //--- Caixas de seleção CCheckBox m_checkbox5; //--- Controles deslizantes CSlider m_slider1; //--- private: //--- Caixas de seleção #define CHECKBOX5_GAP_X (7) #define CHECKBOX5_GAP_Y (200) bool CreateCheckBox5(const string text); //--- Controles deslizantes #define SLIDER1_GAP_X (32) #define SLIDER1_GAP_Y (225) bool CreateSlider1(const string text); };
Nós consideramos o código dos métodos para a criação das caixas de seleção no artigo anterior, é por isso que nós estamos avançando com o método para a criação do elemento CProgram::CreateSlider1(). Usando os métodos CSlider::MinValue() e CSlider::MaxValue(), defina um intervalo no valor de -1 até 1. Defina o passo até a 8ª casa decimal (0.00000001). A disponibilidade irá depender do estado atual da quinta caixa de seleção.
//+------------------------------------------------------------------+ //| Cria o controle deslizante 1 | //+------------------------------------------------------------------+ bool CProgram::CreateSlider1(const string text) { //--- Armazena o ponteiro da janela m_slider1.WindowPointer(m_window1); //--- Coordenadas int x=m_window1.X()+SLIDER1_GAP_X; int y=m_window1.Y()+SLIDER1_GAP_Y; //--- Valor double v=(m_slider1.GetValue()==WRONG_VALUE) ? 0.84615385 : m_slider1.GetValue(); //--- Define as propriedades antes da criação m_slider1.XSize(264); m_slider1.YSize(40); m_slider1.EditXSize(87); m_slider1.MaxValue(1); m_slider1.StepValue(0.00000001); m_slider1.MinValue(-1); m_slider1.SetDigits(8); m_slider1.SetValue(v); m_slider1.AreaColor(clrWhiteSmoke); m_slider1.LabelColor(clrBlack); m_slider1.LabelColorLocked(clrSilver); m_slider1.EditColorLocked(clrWhiteSmoke); m_slider1.EditBorderColor(clrSilver); m_slider1.EditBorderColorLocked(clrSilver); m_slider1.EditTextColorLocked(clrSilver); m_slider1.SlotLineDarkColor(clrSilver); m_slider1.SlotLineLightColor(clrWhite); m_slider1.SlotYSize(4); m_slider1.ThumbColorLocked(clrLightGray); m_slider1.ThumbColorPressed(clrSilver); m_slider1.SlotIndicatorColor(C'85,170,255'); m_slider1.SlotIndicatorColorLocked(clrLightGray); //--- Cria o controle if(!m_slider1.CreateSlider(m_chart_id,m_subwin,text,x,y)) return(false); //--- A disponibilidade irá depender do estado atual da quinta caixa de seleção m_slider1.SliderState(m_checkbox5.CheckButtonState()); //--- Adiciona o objeto ao array comum dos grupos de objetos CWndContainer::AddToElementsArray(0,m_slider1); return(true); }
Os novos métodos para a criação dos elementos devem ser chamados no método principal para a criação da interface gráfica. Abaixo está o código de uma versão resumida deste método:
//+------------------------------------------------------------------+ //| 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 //--- Caixas de seleção if(!CreateCheckBox5("Checkbox 5")) return(false); //--- Controles deslizantes if(!CreateSlider1("Slider 1:")) return(false); //--- Redesenha o gráfico m_chart.Redraw(); return(true); }
Nós vamos controlar a mudança do estado da quinta caixa de seleção para controlar a disponibilidade do controle deslizante no manipulador de eventos CProgram::OnEvent() do aplicativo. O evento com o identificador personalizado ON_END_EDIT irá indicar quando o valor no campo de entrada irá mudar.
//+------------------------------------------------------------------+ //| Manipulador de eventos | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- O evento clique no rótulo de texto if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL) { ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); //--- Se a quinta caixa de seleção foi clicada if(lparam==m_checkbox5.Id()) { //--- Define o estado do primeiro controle deslizante m_slider1.SliderState(m_checkbox5.CheckButtonState()); } } //--- o fim do evento de introdução do valor no campo de entrada if(id==CHARTEVENT_CUSTOM+ON_END_EDIT) { ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); } }
Agora, o programa pode ser compilado e carregado ao gráfico. Tente interagir com os controles da interface gráfica da aplicação. Se tudo foi feito corretamente, você verá um resultado como na imagem abaixo:
Fig. 2. Testando o controle deslizante.
O Controle Deslizante Duplo
A diferença entre um controle deslizante duplo e um simples é que o primeiro permite a seleção na faixa de valor definida pelo usuário. Por esse motivo, existem duas corrediças na linha do controle deslizante. A corrediça esquerda do controle deslizante pode ser movido a partir do lado esquerdo da linha do controle deslizante para a corrediça direita do controle deslizante. A corrediça direita do controle deslizante da direita pode ser movida a partir do lado direito da linha do controle deslizante para a corrediça esquerda do controle deslizante. O controle deslizante duplo também tem dois campos de entrada que refletem o valor em relação à posição de suas corrediças linha do controle deslizante. Os valores podem ser inseridos manualmente nestes campos de entrada. Isso vai mudar a posição das corrediças do controle deslizante.
Este controle será composto por oito objetos gráficos primitivos. Eles são:
- Fundo.
- Título (rótulo de texto)
- Campo de entrada esquerdo.
- Campo de entrada direito.
- Linha do controle deslizante.
- Corrediça esquerda do controle deslizante
- Corrediça direita do controle deslizante
- Indicador do controle deslizante
Fig. 3. Partes integrantes do controle deslizante duplo.
Vamos dar uma olhada na classe do controle deslizante duplo e notar as diferenças do controle deslizante simples.
Desenvolvimento de uma Classe para a Criação do Controle Deslizante Duplo
Inclua o arquivo DualSlider.mqh com a classe do controle CDualSlider no arquivo WndContainer.mqh:
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "DualSlider.mqh"
Em termos das propriedades do controle, a classe CDualSlider é idêntica a classe CSlider. A única diferença aqui é que os campos de entrada da esquerda e direita e as corrediças do controle deslizante exigem campos e métodos distintos. As diferenças entre estes dois métodos são insignificantes e é por isso que nós não vamos discutir o seu código aqui. Você pode encontrá-lo nos arquivos anexados neste artigo.
class CDualSlider : public CElement { private: //--- Valores atuais nos campos de entrada (esquerda e direita) double m_left_edit_value; double m_right_edit_value; //--- Posição atual das corrediças do controle deslizante (esquerda e direita) double m_left_current_pos; double m_left_current_pos_x; double m_right_current_pos; double m_right_current_pos_x; //--- Estado do botão do mouse (pressionado/solto) para os corrediças do controle deslizante (esquerda e direita) ENUM_THUMB_MOUSE_STATE m_clamping_mouse_left_thumb; ENUM_THUMB_MOUSE_STATE m_clamping_mouse_right_thumb; //--- public: //--- Retorna e define o valor dos campos de entrada (esquerda e direita) double GetLeftValue(void) const { return(m_left_edit_value); } double GetRightValue(void) const { return(m_right_edit_value); } bool SetLeftValue(double value); bool SetRightValue(double value); //--- Altera os valores nos campos de entrada (esquerda e direita) void ChangeLeftValue(const double value); void ChangeRightValue(const double value); //--- private: //--- Processo de mover as corrediças do controle deslizante (esquerda e direita) void OnDragLeftThumb(const int x); void OnDragRightThumb(const int x); //--- Atualiza a posição das corrediças do controle deslizante (esquerda e direita) void UpdateLeftThumb(const int new_x_point); void UpdateRightThumb(const int new_x_point); //--- Verifica o estado do botão esquerdo do mouse sobre o segundo controle deslizante void CheckMouseOnLeftThumb(void); void CheckMouseOnRightThumb(void); //--- Calcula a coordenada X dos controles deslizantes (esquerda e direita) void CalculateLeftThumbX(void); void CalculateRightThumbX(void); //--- Altera a posição da corrediça esquerda do controle deslizante em relação ao valor (esquerda e direita) void CalculateLeftThumbPos(void); void CalculateRightThumbPos(void); };
Aqui, nós iremos apresentar apenas o código dos métodos onde foi considerado os dois campos de entrada e as corrediças do controle deslizante. Abaixo encontra-se a estrutura do método CDualSlider::OnEndEdit():
class CDualSlider : public CElement { private: //--- Lidando com a inserção do valor no campo de entrada bool OnEndEdit(const string object_name); }; //+------------------------------------------------------------------+ //| Fim da entrada dos valores | //+------------------------------------------------------------------+ bool CDualSlider::OnEndEdit(const string object_name) { //--- Se o valor é inserido no campo de entrada esquerdo if(object_name==m_left_edit.Name()) { //--- Obter o valor inserido double entered_value=::StringToDouble(m_left_edit.Description()); //--- Verifica, ajusta e armazena o novo valor ChangeLeftValue(entered_value); //--- Calcula a coordenada X da corrediça do controle deslizante CalculateLeftThumbX(); //--- Atualização da localização da corrediça do controle deslizante UpdateLeftThumb(m_left_thumb.X()); //--- Calcula a posição no intervalo de valores CalculateLeftThumbPos(); //--- Verifica, ajusta e armazena o novo valor ChangeLeftValue(m_left_current_pos); //--- Atualiza o indicador do controle deslizante UpdateIndicator(); //--- Envia uma mensagem sobre ele ::EventChartCustom(m_chart_id,ON_END_EDIT,CElement::Id(),CElement::Index(),m_label.Description()); return(true); } //--- Se o valor é inserido no campo de entrada à direita if(object_name==m_right_edit.Name()) { //--- Obter o valor inserido double entered_value=::StringToDouble(m_right_edit.Description()); //--- Verifica, ajusta e armazena o novo valor ChangeRightValue(entered_value); //--- Calcula a coordenada X da corrediça do controle deslizante CalculateRightThumbX(); //--- Atualização da localização da corrediça do controle deslizante UpdateRightThumb(m_right_thumb.X()); //--- Calcula a posição no intervalo de valores CalculateRightThumbPos(); //--- Verifica, ajusta e armazena o novo valor ChangeRightValue(m_right_current_pos); //--- Atualiza o indicador do controle deslizante UpdateIndicator(); //--- Envia uma mensagem sobre ele ::EventChartCustom(m_chart_id,ON_END_EDIT,CElement::Id(),CElement::Index(),m_label.Description()); return(true); } //--- return(false); }
O mesmo aplica-se ao movimento das corrediças esquerda e direita do controle deslizante. O manipulador de evento CDualSlider::OnEvent() contém as verificações e blocos de código separados para cada um deles:
//+------------------------------------------------------------------+ //| Manipulador de eventos do gráfico | //+------------------------------------------------------------------+ void CDualSlider::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; //--- Coordenadas e o estado do botão esquerdo do mouse int x=(int)lparam; int y=(int)dparam; m_mouse_state=(bool)int(sparam); //--- Verificando o foco sobre os elementos CElement::MouseFocus(x>X() && x<X2() && y>Y() && y<Y2()); m_left_thumb.MouseFocus(x>m_left_thumb.X() && x<m_left_thumb.X2() && y>m_left_thumb.Y() && y<m_left_thumb.Y2()); m_right_thumb.MouseFocus(x>m_right_thumb.X() && x<m_right_thumb.X2() && y>m_right_thumb.Y() && y<m_right_thumb.Y2()); //--- Sai, se o elemento está bloqueado if(!m_slider_state) return; //--- Verifica e armazena o estado do botão do mouse CheckMouseOnLeftThumb(); CheckMouseOnRightThumb(); //--- Altera a cor da corrediça do controle deslizante ChangeThumbColor(); //--- Se a gestão foi passada para a linha do controle deslizante (corrediça esquerda do controle deslizante) if(m_clamping_mouse_left_thumb==THUMB_PRESSED_INSIDE) { //--- Movendo a corrediça do controle deslizante OnDragLeftThumb(x); //--- Cálculo da posição da corrediça do controle deslizante na faixa do valor CalculateLeftThumbPos(); //--- Define o novo valor no campo de entrada ChangeLeftValue(m_left_current_pos); //--- Atualiza o indicador do controle deslizante UpdateIndicator(); return; } //--- Se a gestão foi passada para a barra de rolagem (corrediça direita do controle deslizante) if(m_clamping_mouse_right_thumb==THUMB_PRESSED_INSIDE) { //--- Movendo a corrediça do controle deslizante OnDragRightThumb(x); //--- Cálculo da posição da corrediça do controle deslizante na faixa do valor CalculateRightThumbPos(); //--- Define o novo valor no campo de entrada ChangeRightValue(m_right_current_pos); //--- Atualiza o indicador do controle deslizante UpdateIndicator(); return; } } }
Você pode baixar os arquivos anexados neste artigo e estudar mais a fundo o código da classe CDualSlider.
Teste do Controle Deslizante Duplo
Adicione o controle deslizante duplo na interface gráfica da aplicação de teste. Declare uma instância da classe CDualSlider e um método com as margens da borda do formulário na classe personalizada CProgram do aplicativo:
//+------------------------------------------------------------------+ //| Classe para a criação de um aplicativo | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { private: //--- Controles deslizantes CDualSlider m_dual_slider1; //--- private: //--- Controles deslizantes #define DUALSLIDER1_GAP_X (32) #define DUALSLIDER1_GAP_Y (275) bool CreateDualSlider1(const string text); };
O código a seguir mostra o código do método CProgram::CreateDualSlider1(). Defina o intervalo do valor de -2000 até 1000. A disponibilidade deste controle dependerá do estado atual da quinta caixa de seleção semelhante ao controle deslizante simples que foi criado no início deste artigo.
//+------------------------------------------------------------------+ //| Cria o controle deslizante duplo 1 | //+------------------------------------------------------------------+ bool CProgram::CreateDualSlider1(const string text) { //--- Armazena o ponteiro da janela m_dual_slider1.WindowPointer(m_window1); //--- Coordenadas int x=m_window1.X()+DUALSLIDER1_GAP_X; int y=m_window1.Y()+DUALSLIDER1_GAP_Y; //--- Valores double v1=(m_dual_slider1.GetLeftValue()==WRONG_VALUE) ? 0 : m_dual_slider1.GetLeftValue(); double v2=(m_dual_slider1.GetRightValue()==WRONG_VALUE) ? 500 : m_dual_slider1.GetRightValue(); //--- Define as propriedades antes da criação m_dual_slider1.XSize(264); m_dual_slider1.YSize(40); m_dual_slider1.EditXSize(87); m_dual_slider1.MaxValue(1000); m_dual_slider1.StepValue(1); m_dual_slider1.MinValue(-2000); m_dual_slider1.SetDigits(0); m_dual_slider1.SetLeftValue(v1); m_dual_slider1.SetRightValue(v2); m_dual_slider1.AreaColor(clrWhiteSmoke); m_dual_slider1.LabelColor(clrBlack); m_dual_slider1.LabelColorLocked(clrSilver); m_dual_slider1.EditColorLocked(clrWhiteSmoke); m_dual_slider1.EditBorderColor(clrSilver); m_dual_slider1.EditBorderColorLocked(clrSilver); m_dual_slider1.EditTextColorLocked(clrSilver); m_dual_slider1.SlotLineDarkColor(clrSilver); m_dual_slider1.SlotLineLightColor(clrWhite); m_dual_slider1.SlotYSize(4); m_dual_slider1.ThumbColorLocked(clrLightGray); m_dual_slider1.ThumbColorPressed(clrSilver); m_dual_slider1.SlotIndicatorColor(C'85,170,255'); m_dual_slider1.SlotIndicatorColorLocked(clrLightGray); //--- Cria o controle if(!m_dual_slider1.CreateSlider(m_chart_id,m_subwin,text,x,y)) return(false); //--- A disponibilidade irá depender do estado atual da quinta caixa de seleção m_dual_slider1.SliderState(m_checkbox5.CheckButtonState()); //--- Adiciona o objeto ao array comum dos grupos de objetos CWndContainer::AddToElementsArray(0,m_dual_slider1); return(true); }
Não se esqueça de colocar a chamada do método do novo controle no método principal para a criação da interface gráfica da maneira que é demonstrado na versão resumida do código abaixo.
//+------------------------------------------------------------------+ //| 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 //--- Caixas de seleção //--- Controles deslizantes if(!CreateDualSlider1("Dual Slider 1:")) return(false); //--- Redesenha o gráfico m_chart.Redraw(); return(true); }
Agora, o estado atual da quinta caixa de seleção irá definir dois controles - o controle deslizante simples e o deslizante duplo.
//+------------------------------------------------------------------+ //| Manipulador de eventos | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- O evento clique no rótulo de texto if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL) { ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); //--- Se a quinta caixa de seleção foi clicada if(lparam==m_checkbox5.Id()) { //--- Define o estado dos controles deslizantes m_slider1.SliderState(m_checkbox5.CheckButtonState()); m_dual_slider1.SliderState(m_checkbox5.CheckButtonState()); } } //--- o fim do evento de introdução do valor no campo de entrada if(id==CHARTEVENT_CUSTOM+ON_END_EDIT) { ::Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam); } }
Agora, compile o programa e carregue-o ao gráfico. A imagem abaixo ilustra o resultado:
Fig. 4. O controle duplo deslizante.
Conclusão
Na sexta parte da série, nós consideramos seis controles:
- Caixa de seleção (checkbox).
- O controle campo de edição.
- O campo de edição com a caixa de seleção.
- Lista combinada com a caixa de seleção
- Controle deslizante
- Controle duplo deslizante.
A esquemática da biblioteca para a criação das interfaces gráficas no atual estágio de desenvolvimento é parecido com a imagem abaixo:
Fig. 5. Estrutura da biblioteca no atual estágio de desenvolvimento.
Na próxima parte da série, vamos enriquecer a nossa biblioteca com as tabelas e guias.
Você pode baixar o material da parte VI e 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 sexta parte:
- Interfaces Gráficas VI: Os Controles Caixa de Seleção, Campo de Edição e seus Tipos Combinados (Capítulo 1)
- Interfaces Gráficas VI: Os Controles Deslizante e Deslizante Duplo (Capítulo 2)
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2468
- 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