English Русский 中文 Deutsch 日本語 Português
Interfaces gráficas V: Barra de desplazamiento vertical y horizontal (Capítulo 1)

Interfaces gráficas V: Barra de desplazamiento vertical y horizontal (Capítulo 1)

MetaTrader 5Ejemplos | 5 mayo 2016, 14:48
3 587 0
Anatoli Kazharski
Anatoli Kazharski

Índice



Introducción

En los artículos anteriores de la serie sobre las interfaces gráficas en el entorno de los terminales MetaTrader hemos analizado los componentes principales de la librería, también hemos creado varios controles: menú principal, menú contextual, barra de estado, botones, grupos de botones, descripciones emergentes. La quinta parte de la serie será dedicada a los controles como la barra de desplazamiento y la lista En el primer capítulo escribiremos las clases para la creación de la barra de desplazamiento vertical y horizontal. En el segundo capitulo vamos a desarrollar el elemento compuesto de la interfaz “Lista”. Este control va a ser compuesto porque la barra de desplazamiento forma su parte integrante, por eso empezaremos con ella. 

 


Control “Barra de desplazamiento”

Las barras de desplazamiento se utilizan en diferentes listas y tablas cuando el conjunto de datos no cabe en el área especificada. Los objetos principales de una barra de desplazamiento son los siguientes: los botones de flecha para mover el bloque de datos a un paso, y el deslizador (slider) mediante el cual se puede mover rápidamente los datos manteniendo pulsado este elemento deslizante y arrastrándolo.

Vamos a usar cinco objetos gráficos para componer nuestra barra de desplazamiento.

  1. Fondo principal.
  2. Fondo del área de desplazamiento del deslizador.
  3. Dos botones de flecha para mover el bloque de datos a un paso.
  4. Deslizador para mover rápidamente el bloque de datos.

 

Fig. 1. Partes integrantes del control “Barra de desplazamiento”.

Puesto que la barra de desplazamiento puede ser de dos tipos (vertical y horizontal), será conveniente crear una clase separada para cada tipo. Serán las clases derivadas para visualizar las particularidades únicas de cada uno de los tipos de la barra. 

  • CScrollV – clase derivada para la barra vertical.
  • CScrollH – clase derivada para la barra horizontal.

La clase base para ellas será CScroll que va a contener los campos y los métodos comunes para cada tipo. Colocamos las tres clases en el archivo Scrolls.mqh. El esquema para el control “Barra de desplazamiento” será el siguiente:

 Fig. 2. Esquema del control “Barra de desplazamiento”.

Fig. 2. Esquema del control “Barra de desplazamiento”.


A continuación, vamos a analizar el proceso del desarrollo de la clase base de este control (CScroll). 

 


Clase base del control

El control “Barra de desplazamiento” no es un elemento independiente de la interfaz gráfica. Es un elemento auxiliar y habrá que conectarlo a otros controles que van a requerir el desplazamiento de datos dentro del área de trabajo. Por eso no hay que incluir el archivo Scrolls.mqh directamente en el archivo WndContainer.mqh. Las clases de la barra de desplazamiento estarán disponibles en la librería a través de la conexión a los archivos con las clases de otros controles.

Por favor, cree la clase CScroll en el archivo Scrolls.mqh con los métodos estándar para todos los controles de la interfaz:

//+------------------------------------------------------------------+
//|                                                      Scrolls.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//+------------------------------------------------------------------+
//| Clase base para crear la barra de desplazamiento                      |
//+------------------------------------------------------------------+
class CScroll : public CElement
  {
protected:
   //--- Puntero al formulario al que está adjuntado el control
   CWindow          *m_wnd;
   //---
public:
   //--- Guarda el puntero del formulario
   void              WindowPointer(CWindow &object)           { m_wnd=::GetPointer(object);       }
   //---
public:
   //--- Manejador de eventos del gráfico
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Temporizador
   virtual void      OnEventTimer(void) {}
   //--- Desplazamiento del control
   virtual void      Moving(const int x,const int y);
   //--- (1) Mostrar, (2) ocultar, (3) resetear, (4) eliminar
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- (1) Establecer, (2) poner a cero las prioridades para el clic izquierdo del ratón
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
   //--- Resetear color
   virtual void      ResetColors(void) {}
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CScroll::CScroll(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CScroll::~CScroll(void)
  {
  }

Adelantándose un poco, cabe mencionar que en el mismo archivo hay que crear las clases derivadas, tal como se muestra en el código de abajo. En función del tipo de la clase que va a utilizarse, al elemento en el constructor de la clase se le asigna el nombre correspondiente de la clase.  

//+------------------------------------------------------------------+
//| Clase para manejar la barra de deslizamiento vertical              |
//+------------------------------------------------------------------+
class CScrollV : public CScroll
  {
public:
                     CScrollV(void);
                    ~CScrollV(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CScrollV::CScrollV(void)
  {
//--- Guardamos el nombre de la clase del control en la clase base
   CElement::ClassName(CLASS_NAME);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CScrollV::~CScrollV(void)
  {
  }
//+------------------------------------------------------------------+
//| Clase para manejar la barra de deslizamiento horizontal            |
//+------------------------------------------------------------------+
class CScrollH : public CScroll
  {
public:
                     CScrollH(void);
                    ~CScrollH(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CScrollH::CScrollH(void)
  {
//--- Guardamos el nombre de la clase del control en la clase base
   CElement::ClassName(CLASS_NAME);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CScrollH::~CScrollH(void)
  {
  }

Como ya se ha dicho antes, vamos a hacer que haya posibilidad de realizar la configuración precisa de la apariencia de la barra. Para eso, en la clase base (CScroll), vamos a crear los campos y métodos que permiten establecer los siguientes parámetros para los objetos del control:

  • Ancho de la barra de desplazamiento
  • Color del marco del fondo común
  • Color del fondo y del marco del área en la que se desplaza el deslizador de la barra
  • Color del fondo y del marco del deslizador en diferentes estados
  • Imágenes para los botones en diferentes estados que permiten desplazar los datos

Cabe mencionar que el ancho de la barra de deslizamiento (así como para otros objetos) del tipo vertical será el tamaño por el eje X, y para el tipo horizontal , el tamaño por el eje Y. Por defecto, el ancho será igual a 15 píxeles. Por defecto, las imágenes para los botones están ajustadas a este tamaño. Se ubican dentro del fondo común del control con el margen de 1 píxel para no tapar el marco del fondo, y su tamaño será igual a 13x13 píxeles. Por eso, si necesitamos cambiar el ancho de la barra, habrá que reajustar las imágenes para los botones. Para estos fines crearemos los métodos correspondientes.

El ancho para otros objetos de la barra de deslizamiento (área interna y deslizador) va a calcularse automáticamente respecto al ancho del fondo general. El largo también se calcula automáticamente porque este parámetro depende del número de puntos en la lista y su tamaño por el eje Y. Para ver cómo va a implementarse todo eso, lea a continuación. 

class CScroll : public CElement
  {
protected:
   //--- Propiedades de la superficie común de la barra
   int               m_area_width;
   int               m_area_length;
   color             m_area_color;
   color             m_area_border_color;
   //--- Propiedades del fondo debajo del deslizador
   int               m_bg_length;
   color             m_bg_border_color;
   //--- Imágenes para los botones
   string            m_inc_file_on;
   string            m_inc_file_off;
   string            m_dec_file_on;
   string            m_dec_file_off;
   //--- Colores del deslizador en diferentes estados
   color             m_thumb_color;
   color             m_thumb_color_hover;
   color             m_thumb_color_pressed;
   color             m_thumb_border_color;
   color             m_thumb_border_color_hover;
   color             m_thumb_border_color_pressed;
   //--- (1) Ancho del deslizador, (2) largo del deslizador (3) su largo mínimo
   int               m_thumb_width;
   int               m_thumb_length;
   int               m_thumb_min_length;
   //--- (1) Tamaño del paso del deslizador y (2) número de pasos
   double            m_thumb_step_size;
   double            m_thumb_steps_total;
   //--- Prioridades para el clic izquierdo del ratón
   int               m_area_zorder;
   int               m_bg_zorder;
   int               m_arrow_zorder;
   int               m_thumb_zorder;
   //---
public:
  //--- Ancho del deslizador
   void              ScrollWidth(const int width)             { m_area_width=width;               }
   int               ScrollWidth(void)                  const { return(m_area_width);             }
   //--- (1) Color del fondo, (2) del marco del fondo y (3) del marco interno del fondo
   void              AreaColor(const color clr)               { m_area_color=clr;                 }
   void              AreaBorderColor(const color clr)         { m_area_border_color=clr;          }
   void              BgBorderColor(const color clr)           { m_bg_border_color=clr;            }
   //--- Establecer las imágenes para los botones
   void              IncFileOn(const string file_path)        { m_inc_file_on=file_path;          }
   void              IncFileOff(const string file_path)       { m_inc_file_off=file_path;         }
   void              DecFileOn(const string file_path)        { m_dec_file_on=file_path;          }
   void              DecFileOff(const string file_path)       { m_dec_file_off=file_path;         }
   //--- (1) Colores del fondo del deslizador y (2) del marco del deslizador
   void              ThumbColor(const color clr)              { m_thumb_border_color=clr;         }
   void              ThumbColorHover(const color clr)         { m_thumb_border_color_hover=clr;   }
   void              ThumbColorPressed(const color clr)       { m_thumb_border_color_pressed=clr; }
   void              ThumbBorderColor(const color clr)        { m_thumb_border_color=clr;         }
   void              ThumbBorderColorHover(const color clr)   { m_thumb_border_color_hover=clr;   }
   void              ThumbBorderColorPressed(const color clr) { m_thumb_border_color_pressed=clr; }
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CScroll::CScroll(void) : m_area_width(15),
                         m_area_length(0),
                         m_inc_file_on(""),
                         m_inc_file_off(""),
                         m_dec_file_on(""),
                         m_dec_file_off(""),
                         m_thumb_width(0),
                         m_thumb_length(0),
                         m_thumb_min_length(15),
                         m_area_color(C'210,210,210'),
                         m_area_border_color(C'240,240,240'),
                         m_bg_border_color(C'210,210,210'),
                         m_thumb_color(C'190,190,190'),
                         m_thumb_color_hover(C'180,180,180'),
                         m_thumb_color_pressed(C'160,160,160'),
                         m_thumb_border_color(C'170,170,170'),
                         m_thumb_border_color_hover(C'160,160,160'),
                         m_thumb_border_color_pressed(C'140,140,140')
  {
//--- Establecemos las prioridades para el clic izquierdo del ratón
   m_area_zorder  =8;
   m_bg_zorder    =9;
   m_arrow_zorder =10;
   m_thumb_zorder =11;
  }

Vamos a considerar los métodos de la creación del control “Barra de desplazamiento”. Cada una de sus partes se crea usando un método privado individual. En el método principal, hay que pasar el tamaño de la lista y el tamaño de la parte visible de la lista como dos últimos parámetros. Luego estos parámetros van a participar en el cálculo del número de pasos para el deslizador del la barra de deslizamiento. 

class CScroll : public CElement
  {
protected:
   //--- Objetos para crear la barra de desplazamiento
   CRectLabel        m_area;
   CRectLabel        m_bg;
   CBmpLabel         m_inc;
   CBmpLabel         m_dec;
   CRectLabel        m_thumb;
   //---
public:
   //--- Métodos para crear la barra de desplazamiento
   bool              CreateScroll(const long chart_id,const int subwin,const int x,const int y,const int items_total,const int visible_items_total);
   //---
private:
   bool              CreateArea(void);
   bool              CreateBg(void);
   bool              CreateInc(void);
   bool              CreateDec(void);
   bool              CreateThumb(void);
  };

Como ejemplo vamos a mostrar el código de algunos de estos métodos. En los demás se utilizan los principios parecidos, y no será muy difícil aclararlos por su propia cuenta. Mire el código del método CScroll::CreateBg() que está más abajo. Fíjense en que la formación del nombre del objeto depende del tipo de la barra y de que si existe el índice para este elemento. La especificación del índice puede ser necesaria durante el desarrollo de los controles compuestos complejos en los que pueden utilizarse varias barras de desplazamiento del mismo tipo. Mostraremos estos ejemplos en uno de los siguientes artículos.

Además, los cálculos y determinación de los valores de los siguientes parámetros también dependen del tipo de la barra: (1) coordenadas, (2) largo del área de desplazamiento del deslizador y los (3) tamaños. Esta parte está marcada con color azul en el código de abajo. 

//+------------------------------------------------------------------+
//| Crea el fondo del scroll                                              |
//+------------------------------------------------------------------+
bool CScroll::CreateBg(void)
  {
//--- Formación del nombre del objeto
   string name      ="";
   string name_part =(CElement::ClassName()=="CScrollV")? "_scrollv_bg_" : "_scrollh_bg_";
//--- El índice no está definido
   if(CElement::Index()==WRONG_VALUE)
      name=CElement::ProgramName()+name_part+(string)CElement::Id();
//--- Si el índice está definido
   else
      name=CElement::ProgramName()+name_part+(string)CElement::Index()+"__"+(string)CElement::Id();
//--- Coordenadas
   int x=0;
   int y=0;
//--- Tamaños
   int x_size=0;
   int y_size=0;
//--- Establecer las propiedades tomando en cuenta el tipo del scroll
   if(CElement::ClassName()=="CScrollV")
     {
      m_bg_length =CElement::YSize()-(m_thumb_width*2)-2;
      x           =CElement::X()+1;
      y           =CElement::Y()+m_thumb_width+1;
      x_size      =m_thumb_width;
      y_size      =m_bg_length;
     }
   else
     {
      m_bg_length =CElement::XSize()-(m_thumb_width*2)-2;
      x           =CElement::X()+m_thumb_width+1;
      y           =CElement::Y()+1;
      x_size      =m_bg_length;
      y_size      =m_thumb_width;
     }
//--- Creación del objeto
   if(!m_bg.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size))
      return(false);
//--- Establecemos las propiedades
   m_bg.BackColor(m_area_color);
   m_bg.Color(m_bg_border_color);
   m_bg.BorderType(BORDER_FLAT);
   m_bg.Corner(m_corner);
   m_bg.Selectable(false);
   m_bg.Z_Order(m_bg_zorder);
   m_bg.Tooltip("\n");
//--- Guardamos las coordenadas
   m_bg.X(x);
   m_bg.Y(y);
//--- Guardamos los márgenes
   m_bg.XGap(x-m_wnd.X());
   m_bg.YGap(y-m_wnd.Y());
//--- Guardamos los tamaños
   m_bg.XSize(x_size);
   m_bg.YSize(y_size);
//--- Guardamos el puntero del objeto
   CElement::AddToArray(m_bg);
   return(true);
  }

Al crear el objeto gráfico que va a servir del deslizador de la barra de desplazamiento, vamos a necesitar el método para calcular su largo. Este parámetro (largo) depende del número de puntos en la lista y del número de puntos en su parte visible. Vamos a crear este método y llamarlo CScroll::CalculateThumbSize(). 

Al principio de este método, se realiza la comprobación del tamaño del área de desplazamiento del deslizador. Si el largo de esta área es menor que el largo mínimo del deslizador, no hace falta realizar el cálculo. El método devolverá false y el deslizador no será creado, porque en esta situación simplemente no es necesario. Si la comprobación ha sido superada, el cálculo se realiza en varios pasos:

  • Se calcula el tamaño del paso del deslizador. 
  • Se resulta que el valor obtenido es menor que 1, lo corregimos hasta que sea igual a 1.
  • Se calcula el tamaño del área de trabajo para el desplazamiento del deslizador. 
  • Si el tamaño del área de trabajo resulta ser menos que el tamaño del área total para el desplazamiento del deslizador, calculamos el largo del deslizador. En caso contrario, establecemos el largo mínimo predefinido igual a 15 píxeles.
  • Al final de todo, se realiza la comprobación del tamaño obtenido del deslizador con la conversión al tipo entero (int) y su corrección si el largo es menor que el mínimo. 

Abajo se muestran más detalles del código del método CScroll::CalculateThumbSize():

class CScroll : public CElement
  {
protected:

public:
   //--- Cálculo del largo del deslizador de la barra
   bool              CalculateThumbSize(void);
  };
//+------------------------------------------------------------------+
//| Cálculo del tamaño de la barra de deplazamiento                                  |
//+------------------------------------------------------------------+
bool CScroll::CalculateThumbSize(void)
  {
//--- El cálculo no es necesario si el largo del área de desplazamiento del deslizador es menor que el largo mínimo del deslizador
   if(m_bg_length<m_thumb_min_length)
      return(false);
//--- Calculamos el tamaño del paso del deslizador
   m_thumb_step_size=(double)(m_bg_length-m_thumb_min_length)/m_thumb_steps_total;
//--- El tamaño del paso no puede ser menos de 1
   m_thumb_step_size=(m_thumb_step_size<1)? 1 : m_thumb_step_size;
//--- Calculamos el tamaño del área de trabajo para el desplazamiento del deslizador
   double work_area=m_thumb_step_size*m_thumb_steps_total;
//--- Si el tamaño del área de trabajo es menor que el tamaño del área entera, obtenemos el tamaño del deslizador, de lo contrario establecemos el tamaño mínimo
   double thumb_size=(work_area<m_bg_length)? m_bg_length-work_area+m_thumb_step_size : m_thumb_min_length;
//--- Comprobar el tamaño del deslizador con la conversión del tipo
   m_thumb_length=((int)thumb_size<m_thumb_min_length)? m_thumb_min_length :(int)thumb_size;
   return(true);
  }

Hay que llamar al método CScroll::CalculateThumbSize() en el método CScroll::CreateThumb() antes de crear el objeto. Más tarde mostraremos otra situación cuando hay que calcular el largo de la barra de desplazamiento en el proceso del uso del control. Lo veremos en el proceso del desarrollo del control donde sea necesario.

//+------------------------------------------------------------------+
//| Crea el deslizador de la barra de desplazamiento                                |
//+------------------------------------------------------------------+
bool CScroll::CreateThumb(void)
  {
//--- Formación del nombre del objeto  
   string name      ="";
   string name_part =(CElement::ClassName()=="CScrollV")? "_scrollv_thumb_" : "_scrollh_thumb_";
//--- El índice no está definido
   if(CElement::Index()==WRONG_VALUE)
      name=CElement::ProgramName()+name_part+(string)CElement::Id();
//--- Si el índice está definido
   else
      name=CElement::ProgramName()+name_part+(string)CElement::Index()+"__"+(string)CElement::Id();
//--- Coordenadas
   int x=0;
   int y=0;
//--- Tamaños
   int x_size=0;
   int y_size=0;
//--- Calculamos el tamaño de la barra de deplazamiento
   if(!CalculateThumbSize())
      return(true);
//--- Establecer la propiedad tomando en cuenta el tipo del scroll
   if(CElement::ClassName()=="CScrollV")
     {
      x      =(m_thumb.X()>0) ? m_thumb.X() : m_x+1;
      y      =(m_thumb.Y()>0) ? m_thumb.Y() : m_y+m_thumb_width+1;
      x_size =m_thumb_width;
      y_size =m_thumb_length;
     }
   else
     {
      x      =(m_thumb.X()>0) ? m_thumb.X() : m_x+m_thumb_width+1;
      y      =(m_thumb.Y()>0) ? m_thumb.Y() : m_y+1;
      x_size =m_thumb_length;
      y_size =m_thumb_width;
     }
//--- Creación del objeto
   if(!m_thumb.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size))
      return(false);
//--- Establecemos las propiedades
   m_thumb.BackColor(m_thumb_color);
   m_thumb.Color(m_thumb_border_color);
   m_thumb.BorderType(BORDER_FLAT);
   m_thumb.Corner(m_corner);
   m_thumb.Selectable(false);
   m_thumb.Z_Order(m_thumb_zorder);
   m_thumb.Tooltip("\n");
//--- Guardamos las coordenadas
   m_thumb.X(x);
   m_thumb.Y(y);
//--- Guardamos los márgenes
   m_thumb.XGap(x-m_wnd.X());
   m_thumb.YGap(y-m_wnd.Y());
//--- Guardamos los tamaños
   m_thumb.XSize(x_size);
   m_thumb.YSize(y_size);
//--- Guardamos el puntero del objeto
   CElement::AddToArray(m_thumb);
   return(true);
  }

Otros métodos de la creación de los objetos de la barra de desplazamiento se ofrecen para el aprendizaje individual en el archivo adjunto al artículo.

En el método principal (público) de la creación de la barra de desplazamiento, añadimos la comprobación del tipo de la clase. El nombre de la clase no va a guardarse en la clase base del control (CScroll). Por eso al intentar crear la barra usando la clase base, la creación de la interfaz gráfica se interrumpe y en el diario se mostrará la ayuda diciendo que hay que usar las clases derivadas tipo CScrollV o CScrollH. Preste atención en cómo se inicializan algunos parámetros del control. Esta técnica permite automatizar al máximo todos los cálculos, librando al usuario de la librería de la rutina, y ofreciendo la posibilidad de indicar el número mínimo de propiedades durante la creación de los controles con la barra de desplazamiento. 

//+------------------------------------------------------------------+
//| Crea la barra de desplazamiento                                         |
//+------------------------------------------------------------------+
bool CScroll::CreateScroll(const long chart_id,const int subwin,const int x,const int y,const int items_total,const int visible_items_total)
  {
//--- Salir si no hay puntero al formulario
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Antes de crear el scroll, hay que pasar a la clase "
              "el puntero al formuñario: CScroll::WindowPointer(CWindow &object)");
      return(false);
     }
//--- Salir si se realiza el intento de usar la clase base de la barra
   if(CElement::ClassName()=="")
     {
      ::Print(__FUNCTION__," > Utilice las clases derivadas de la barra (CScrollV o CScrollH).");
      return(false);
     }
//--- Inicialización de variables
   m_chart_id          =chart_id;
   m_subwin            =subwin;
   m_x                 =x;
   m_y                 =y;
   m_area_width        =(CElement::ClassName()=="CScrollV")? CElement::XSize() : CElement::YSize();
   m_area_length       =(CElement::ClassName()=="CScrollV")? CElement::YSize() : CElement::XSize();
   m_thumb_width       =m_area_width-2;
   m_thumb_steps_total =items_total-visible_items_total+1;
//--- Márgenes desde el punto extremo
   CElement::XGap(m_x-m_wnd.X());
   CElement::YGap(m_y-m_wnd.Y());
//--- Crear el botón
   if(!CreateArea())
      return(false);
   if(!CreateBg())
      return(false);
   if(!CreateInc())
      return(false);
   if(!CreateDec())
      return(false);
   if(!CreateThumb())
      return(false);
//--- Ocultar el elemento si es la ventana de diálogo o está minimizada
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      Hide();
//---
   return(true);
  }

Para determinar el estado del botón izquierdo del ratón respecto al deslizador de la barra de desplazamiento, vamos a crear la enumeración ENUM_THUMB_MOUSE_STATE в файле Enums.mqh.

//+------------------------------------------------------------------+
//| Enumeración de los estados del botón izquierdo del ratón para la barra de desplazamiento     |
//+------------------------------------------------------------------+
enum ENUM_THUMB_MOUSE_STATE
  {
   THUMB_NOT_PRESSED     =0,
   THUMB_PRESSED_OUTSIDE =1,
   THUMB_PRESSED_INSIDE  =2
  };

Además, hay que crear los campos y los métodos correspondientes. El método CScroll::CheckMouseButtonState() es necesario para determinar sobre qué área ha sido apretado el botón izquierdo del ratón. El método CScroll::ZeroThumbVariables() se utiliza para poner a cero las variables relacionadas con el desplazamiento del deslizador, y se llama en el método CScroll::CheckMouseButtonState() según la condición cuando el botón izquierdo está desapretado.

Fíjense también en la situación cuando la barra de desplazamiento se pasa al modo de desplazamiento del deslizador, el formulario se bloquea, y eso se hace sólo cuando el control no es desplegable. Es necesario para evitar los modos de resalteo de otros controles al situar el cursor sobre ellos en el momento cuando el deslizador de la barra se encuentra en el modo de desplazamiento y el cursor ha salido fuera de sus límites. Cuando el modo de desplazamiento del deslizador está desactivado, hay que desbloquear el formulario en el método CScroll::ZeroThumbVariables()

class CScroll : public CElement
  {
protected:
   //--- Variables relacionadas con el desplazamiento del deslizador
   bool              m_scroll_state;
   int               m_thumb_size_fixing;
   int               m_thumb_point_fixing;
  //--- Para determinar el área de presión del botón izquierdo del ratón
   ENUM_THUMB_MOUSE_STATE m_clamping_area_mouse;
   //---
public:
  //--- Determina el área de presión del botón izquierdo del ratón
   void              CheckMouseButtonState(const bool mouse_state);
   //--- Puesta a cero de las variables
   void              ZeroThumbVariables(void);
  };
//+------------------------------------------------------------------+
//| Determina el área de presión del botón izquierdo del ratón                     |
//+------------------------------------------------------------------+
void CScroll::CheckMouseButtonState(const bool mouse_state)
  {
//--- Determinamos el estado del botón del ratón
    //--- Si el botón está suelto
   if(!mouse_state)
     {
      //--- Ponemos las variables a cero
      ZeroThumbVariables();
      return;
     }
//--- Si el botón está pulsado
   if(mouse_state)
     {
      //--- Salimos si el botón ya se mantiene pulsado en algún lugar
      if(m_clamping_area_mouse!=THUMB_NOT_PRESSED)
         return;
       //--- Fuera del área del deslizador de la barra
      if(!m_thumb.MouseFocus())
         m_clamping_area_mouse=THUMB_PRESSED_OUTSIDE;
      //--- Dentro del área del deslizador de la barra
      else
        {
         m_clamping_area_mouse=THUMB_PRESSED_INSIDE;
         //--- Si el control no es desplegable
         if(!CElement::IsDropdown())
           {
            //--- Bloqueamos el formulario y guardamos el identificador del elemento activo
            m_wnd.IsLocked(true);
            m_wnd.IdActivatedElement(CElement::Id());
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Poner a cero las variables relacionadas con el desplazamiento del deslizador           |
//+------------------------------------------------------------------+
void CScroll::ZeroThumbVariables(void)
  {
//--- Si el control no es desplegable
   if(!CElement::IsDropdown())
     {
      //--- Desbloqueamos el formulario y reseteamos el identificador del elemento activo
      m_wnd.IsLocked(false);
      m_wnd.IdActivatedElement(WRONG_VALUE);
     }
   m_thumb_size_fixing   =0;
   m_clamping_area_mouse =THUMB_NOT_PRESSED;
  }

Para cambiar el color de objetos de la barra de desplazamiento dependiendo de la posición del cursor y el estado de su botón izquierdo, creamos el método CScroll::ChangeObjectsColor(). En el inicio de este método se comprueba si está bloqueado el formulario y si se diferencian los identificadores de la barra y el identificador que se guarda en la memoria del formulario. Si ambas condiciones se cumplen, entonces dependiendo del modo actual de la barra y el foco sobre sus botones, para ellos se establece el estado correspondiente. La barra de desplazamiento puede tener dos modos: (1) — libre y (2) — en el proceso de desplazamiento del deslizador. Después de eso, dependiendo de la posición del cursor y del área donde ha sido pulsado el botón izquierdo, se establece el color correspondiente para el deslizador de la barra de desplazamiento. Aquí mismo se determina el modo de la barra de desplazamiento.

class CScroll : public CElement
  {
public:
   //--- Cambio del color de objetos de la barra
   void              ChangeObjectsColor(void);
  };
//+------------------------------------------------------------------+
//| Cambia el color de objetos de la barra de la lista                   |
//+------------------------------------------------------------------+
void CScroll::ChangeObjectsColor(void)
  {
//--- Salir si el formulario está bloqueado y el identificador del elemento activo en este momento no coincide
   if(m_wnd.IsLocked() && m_wnd.IdActivatedElement()!=CElement::Id())
      return;
//--- Color de los botones de la barra de la lista
   if(!m_scroll_state)
     {
      m_inc.State(m_inc.MouseFocus());
      m_dec.State(m_dec.MouseFocus());
     }
//--- Si el cursor está en el área de la barra
   if(m_thumb.MouseFocus())
     {
      //--- Si el botón izquierdo del ratón está suelto
      if(m_clamping_area_mouse==THUMB_NOT_PRESSED)
        {
         m_scroll_state=false;
         m_thumb.BackColor(m_thumb_color_hover);
         m_thumb.Color(m_thumb_border_color_hover);
        }
      //--- El botón izquierdo se mantiene pulsado sobre el deslizador
      else if(m_clamping_area_mouse==THUMB_PRESSED_INSIDE)
        {
         m_scroll_state=true;
         m_thumb.BackColor(m_thumb_color_pressed);
         m_thumb.Color(m_thumb_border_color_pressed);
        }
     }
//--- Si el curso está fuera del área de la barra
   else
     {
      //--- El botón izquierdo del ratón está suelto
      if(m_clamping_area_mouse==THUMB_NOT_PRESSED)
        {
         m_scroll_state=false;
         m_thumb.BackColor(m_thumb_color);
         m_thumb.Color(m_thumb_border_color);
        }
     }
  }

Durante el desarrollo de los controles donde va a utilizarse la barra de desplazamiento, nos harán falta los métodos para obtener los nombres y estados de sus botones. Aparte de eso, vamos a necesitar los métodos para establecer y determinar el estado de desplazamiento del deslizador de la barra de desplazamiento y su posición actual respecto a la lista al que está adjuntado. 

class CScroll : public CElement
  {
protected:
   //--- Determinar el estado de desplazamiento
   bool              m_scroll_state;
   //--- Posición actual del deslizador
   int               m_current_pos;
   //---
public:
   //--- Nombres de los objetos de los botones
   string            ScrollIncName(void)                const { return(m_inc.Name());             }
   string            ScrollDecName(void)                const { return(m_dec.Name());             }
   //--- Estado de botones
   bool              ScrollIncState(void)               const { return(m_inc.State());            }
   bool              ScrollDecState(void)               const { return(m_dec.State());            }
   //--- Estado de la barra de desplazamiento
   void              ScrollState(const bool scroll_state)     { m_scroll_state=scroll_state;      }
   bool              ScrollState(void)                  const { return(m_scroll_state);           }
   //--- Posición actual del deslizador
   void              CurrentPos(const int pos)                { m_current_pos=pos;                }
   int               CurrentPos(void)                   const { return(m_current_pos);            }
  };

Hemos terminado el desarrollo de la clase base para la barra de desplazamiento. A continuación, vamos a llenar las clases derivadas de este control. 

 


Clases derivadas del control

Si la clase base de la barra de desplazamiento está llenada con los métodos comunes para crear los objetos, configurar parámetros y obtener sus valores, las clases derivadas están destinadas para manejar este control. Antes ya hemos creado las clases derivadas CScrollV y CScrollH en el archivo Scrolls.mqh. Vamos a considerar los métodos sólo de una de ellas- del tipo vertical (CScrollV). En la segunda clase (CScrollH) va a haber lo mismo, pero con la corrección de que se trata del tipo horizontal. Es decir, si en la clase de la barra vertical trabajamos con la coordenada Y, en la clase del tipo horizontal de este control utilizamos la coordenada X.

Primero analizaremos el código de los métodos CScrollV::OnDragThumb() y CScrollV::UpdateThumb() que sirven para el desplazamiento del deslizador de la barra. Hay que declararlos en la sección privada de la clase porque van a utilizarse sólo dentro de la clase CScrollV.

//+------------------------------------------------------------------+
//| Clase para manejar la barra de deslizamiento vertical              |
//+------------------------------------------------------------------+
class CScrollV : public CScroll
  {
private:
   //--- Desplazamiento del deslizador
   void              OnDragThumb(const int y);
   //--- Actualización de la posición del deslizador
   void              UpdateThumb(const int new_y_point);
  };

El método CScrollV::OnDragThumb() es necesario para determinar que el usuario realiza el intento de desplazar el deslizador. Si el usuario ha hecho clic con el botón izquierdo en el deslizador de la barra, y manteniéndolo pulsado ha desplazado el cursor a lo largo del eje Y, eso significa que el proceso de desplazamiento ha comenzado y hay que mover el objeto. La actualización de las coordenadas del deslizador se realiza a través del método CScrollV::UpdateThumb().  

//+------------------------------------------------------------------+
//| Desplazamiento del deslizador                                             |
//+------------------------------------------------------------------+
void CScrollV::OnDragThumb(const int y)
  {
//--- Para determinar la nueva coordenada Y
   int new_y_point=0;
//--- Si la barra de desplazamiento no está activa, ...
   if(!CScroll::ScrollState())
     {
      //--- ...ponemos a cero las variables auxiliares para el desplazamiento del deslizador
      CScroll::m_thumb_size_fixing  =0;
      CScroll::m_thumb_point_fixing =0;
      return;
     }
//--- Si el punto de fijación es cero, recordamos la coordenada actual del cursor
   if(CScroll::m_thumb_point_fixing==0)
      CScroll::m_thumb_point_fixing=y;
//--- Si el valor de la distancia desde el punto extremo del deslizador hasta la coordenada actual del cursor es cero, lo calculamos
   if(CScroll::m_thumb_size_fixing==0)
      CScroll::m_thumb_size_fixing=m_thumb.Y()-y;
//--- Si en el estado pulsado hemos pasado el umbral hacia abajo
   if(y-CScroll::m_thumb_point_fixing>0)
     {
      //--- Calculamos la coordenada Y
      new_y_point=y+CScroll::m_thumb_size_fixing;
      //--- Reseteamos la posición del deslizador
      UpdateThumb(new_y_point);
      return;
     }
//--- Si en el estado pulsado hemos pasado el umbral hacia arriba
   if(y-CScroll::m_thumb_point_fixing<0)
     {
      //--- Calculamos la coordenada Y
      new_y_point=y-::fabs(CScroll::m_thumb_size_fixing);
      //--- Reseteamos la posición del deslizador
      UpdateThumb(new_y_point);
      return;
     }
  }

El código del método CScrollV::UpdateThumb() se muestra con detalles a continuación. Se le pasa la coordenada Y calculada en el método CScrollV::OnDragThumb().Aquí se comprueba si es correcta. Si resulta que salimos fuera del área de trabajo destinada para el desplazamiento del deslizador, el valor de corrige. Sólo después de eso las coordenadas se actualizan y se guardan en los campos de las clases base. 

//+------------------------------------------------------------------+
//| Actualización de la posición del deslizador                                    |
//+------------------------------------------------------------------+
void CScrollV::UpdateThumb(const int new_y_point)
  {
   int y=new_y_point;
//--- Ponemos a cero el punto de fijación
   CScroll::m_thumb_point_fixing=0;
//--- Comprobar la superación del rango del área de trabajo hacia abajo y corregir los valores
   if(new_y_point>m_bg.Y2()-CScroll::m_thumb_length)
     {
      y=m_bg.Y2()-CScroll::m_thumb_length;
      CScroll::CurrentPos(int(CScroll::m_thumb_steps_total));
     }
//--- Comprobar la superación del rango del área de trabajo hacia arriba y corregir los valores
   if(new_y_point<=m_bg.Y())
     {
      y=m_bg.Y();
      CScroll::CurrentPos(0);
     }
//--- Actualizamos las coordenadas y los márgenes
   m_thumb.Y(y);
   m_thumb.Y_Distance(y);
   m_thumb.YGap(m_thumb.Y()-(CElement::Y()-CElement::YGap()));
  }

Vamos a necesitar un método privado más: para corregir la posición actual del deslizador respecto a su coordenada Y. Lo llamaremos CScrollV::CalculateThumbPos(), abajo puede ver su código. 

class CScrollV : public CScroll
  {
private:
   //--- Corrige el número de la posición del deslizador
   void              CalculateThumbPos(void);
  };
//+------------------------------------------------------------------+
//| Corrige el número de la posición del deslizador                              |
//+------------------------------------------------------------------+
void CScrollV::CalculateThumbPos(void)
  {
//--- Salir si el paso es igual a cero
   if(CScroll::m_thumb_step_size==0)
      return;
//--- Corrige el número de la posición de la barra de desplazamiento
   CScroll::CurrentPos(int((m_thumb.Y()-m_bg.Y())/CScroll::m_thumb_step_size));
//--- Comprobar la superación del rango del área de trabajo hacia arriba/hacia abajo
   if(m_thumb.Y2()>=m_bg.Y2()-1)
      CScroll::CurrentPos(int(CScroll::m_thumb_steps_total-1));
   if(m_thumb.Y()<m_bg.Y())
      CScroll::CurrentPos(0);
  }

Ahora vamos a crear el método público CScrollV::ScrollBarControl() para manejar el deslizador, en el que van a llamarse todos los métodos privados mostrados anteriormente. Hay que llamar a este método en los manejadores de eventos OnEvent() de los controles donde va a utilizarse la barra de desplazamiento. Analizaremos el ejemplo más detallado de cómo va a funcionar eso en el segundo capítulo donde vamos a hablar de la clase para la creación del control “Lista”. 

Abajo se muestra el código del método CScrollV::ScrollBarControl(). Como argumentos se le pasan las coordenadas del cursor y el estado del botón izquierdo del ratón. Al principio se comprueba el foco sobre el deslizador de la barra. Luego se determina sobre qué área ha sido pulsado el botón izquierdo del ratón. En función de la posición del cursor y el estado del botón izquierdo se cambia el color del deslizador. Después de eso, si el control se pasa a la barra de deslizamiento, el deslizador se desplaza según la nueva coordenada calculada Y, y en relación a ella se calcula el número de la posición en la lista. 

class CScrollV : public CScroll
  {
public:
   //--- Manejo del deslizador
   bool              ScrollBarControl(const int x,const int y,const bool mouse_state);
  };
//+------------------------------------------------------------------+
//| Manejo del deslizador                                             |
//+------------------------------------------------------------------+
bool CScrollV::ScrollBarControl(const int x,const int y,const bool mouse_state)
  {
//--- Comprobación del foco sobre el deslizador
   m_thumb.MouseFocus(x>m_thumb.X() && x<m_thumb.X2() && 
                      y>m_thumb.Y() && y<m_thumb.Y2());
//--- Comprobamos y recordamos el estado del botón del ratón
   CScroll::CheckMouseButtonState(mouse_state);
//--- Cambiamos el color del deslizador
   CScroll::ChangeObjectsColor();
//--- Si el control se pasa a la barra de desplazamiento, determinamos la posición del deslizador
   if(CScroll::ScrollState())
     {
       //--- Desplazamiento del deslizador
      OnDragThumb(y);
       //--- Cambia el número de la posición del deslizador
      CalculateThumbPos();
      return(true);
     }
//---
   return(false);
  }

Si el método CScrollV::CalculateThumbPos() convierte la coordenada Y en el número de la posición en la lista, entonces aparte de los demás vamos a necesitar el método que hace la conversión contraria, es decir para calcular la coordenada Y respecto a la posición actual del deslizador. Aquí también se encuentran las comprobaciones de la salida fuera del área de trabajo y la corrección en caso de la salida. Al final del método, las coordenadas y los márgenes se actualizan. 

class CScrollV : public CScroll
  {
public:
  //--- Cálculo de la coordenada Y del deslizador
   void              CalculateThumbY(void);
  };
//+------------------------------------------------------------------+
//| Cálculo de la coordenada Y del deslizador de la barra de desplazamiento                    |
//+------------------------------------------------------------------+
void CScrollV::CalculateThumbY(void)
  {
//--- Determinamos la coordenada actual Y del deslizador
   int scroll_thumb_y=int(m_bg.Y()+(CScroll::CurrentPos()*CScroll::m_thumb_step_size));
//--- Si salimos fuera del área de trabajo hacia arriba
   if(scroll_thumb_y<=m_bg.Y())
      scroll_thumb_y=m_bg.Y();
//--- Si salimos fuera del área de trabajo hacia abajo
   if(scroll_thumb_y+CScroll::m_thumb_length>=m_bg.Y2() || 
      CScroll::CurrentPos()>=CScroll::m_thumb_steps_total-1)
     {
      scroll_thumb_y=int(m_bg.Y2()-CScroll::m_thumb_length);
     }
//--- Actualizamos las coordenadas y los márgenes por el eje Y
   m_thumb.Y(scroll_thumb_y);
   m_thumb.Y_Distance(scroll_thumb_y);
   m_thumb.YGap(m_thumb.Y()-m_wnd.Y());
  }

El método CScrollV::CalculateThumbY() va a llamarse en los métodos de procesamiento del clic en los botones de la barra de desplazamiento (véase el código de abajo). Los métodos CScrollV::OnClickScrollInc() y CScrollV::OnClickScrollDec() reciben el nombre del objeto como argumento único. Al principio se comprueban los siguientes parámetros.

  • Si ha tenido lugar el clic precisamente en este botón. Comprobación del nombre del objeto.
  • Estado de la barra de desplazamiento. Para superar la comprobación, tiene que haber el modo “libre”.
  • El número de los pasos tiene que estar determinado, y este valor no puede ser menos de 1.

Si todas las comprobaciones han sido superadas, a continuación se realiza el desplazamiento de la posición a un paso sin poder salir fuera del área de trabajo. Después de eso, con el método CScrollV::CalculateThumbY() se calcula y se actualiza en el objeto la coordenada Y y los márgenes desde el punto extremos del formulario. Después del clic, el botón debe permanecer en el estado “activado”.

class CScrollV : public CScroll
  {
public:
   //--- Procesamiento del clic en los botones de la barra de desplazamiento
   bool              OnClickScrollInc(const string clicked_object);
   bool              OnClickScrollDec(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| Procesamiento del clic en el botón hacia arriba/a la izquierda                          |
//+------------------------------------------------------------------+
bool CScrollV::OnClickScrollInc(const string clicked_object)
  {
//--- Salimos si el clic ha sido hecho fuera del objeto o el scroll está activo o el número de pasos no está determinado
   if(m_inc.Name()!=clicked_object || CScroll::ScrollState() || CScroll::m_thumb_steps_total<1)
      return(false);
//--- Disminuimos el número de la posición de la barra de desplazamiento
   if(CScroll::CurrentPos()>0)
      CScroll::m_current_pos--;
//--- Cálculo de la coordenada Y de la barra
   CalculateThumbY();
//--- Establecemos el estado On
   m_inc.State(true);
   return(true);
  }
//+------------------------------------------------------------------+
//| Procesamiento del clic en el botón hacia abajo/a la derecha                          |
//+------------------------------------------------------------------+
bool CScrollV::OnClickScrollDec(const string clicked_object)
  {
//--- Salimos si el clic ha sido hecho fuera del objeto o el scroll está activo o el número de pasos no está determinado
   if(m_dec.Name()!=clicked_object || CScroll::ScrollState() || CScroll::m_thumb_steps_total<1)
      return(false);
//--- Aumentamos el número de la posición de la barra de desplazamiento
   if(CScroll::CurrentPos()<CScroll::m_thumb_steps_total-1)
      CScroll::m_current_pos++;
//--- Cálculo de la coordenada Y de la barra
   CalculateThumbY();
//--- Establecemos el estado On
   m_dec.State(true);
   return(true);
  }

Aquí terminamos el desarrollo de todos los métodos necesarios para el manejo de la barra de desplazamiento vertical. Para el tipo horizontal, los métodos son los mismos, a diferencia de que los cálculos se realizan con la coordenada X

 


Conclusión

En este artículo (el primer capítulo de la quinta parte) hemos analizado los controles como la barra de desplazamiento vertical y horizontal. En el segundo capítulo de la quinta parte nos ocuparemos del desarrollo del elemento de la interfaz gráfica “Lista”. Puede que el número de los puntos de la lista no quepan en el espacio asignado. Precisamente en estas situaciones vamos a necesitar el control “Barra de desplazamiento” que hemos desarrollado en este artículo.

Más abajo puede descargar el material de la quinta parte de la serie para poder probar cómo funciona todo eso. Si le surgen algunas preguntas sobre el uso del material de estos archivos, puede dirigirse a la descripción detallada del proceso de desarrollo de la librería en uno de los artículos listados más abajo, o bien hacer su pregunta en los comentarios para el artículo.

Lista de artículos (capítulos) de la quinta parte:

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/2379

Archivos adjuntos |
Kit del trader: Indicadores para el diseño Kit del trader: Indicadores para el diseño
Este artículo incluye las principales tareas para el diseño de los indicadores, así como soluciones y automatizaciones.
Alerta y comentario para indicadores externos. Alerta y comentario para indicadores externos.
En un trabajo práctico, un trader puede encontrarse con la siguiente situación: es necesario conseguir una "alerta" o un mensaje de texto en una pantalla (en una ventana de gráfico) que indique de la aparición de una señal de un indicador. El artículo contiene un ejemplo de muestra de información sobre los objetos gráficos creados por un indicador externo.
Superposición e interferencia de los instrumentos financieros Superposición e interferencia de los instrumentos financieros
Cuantos más factores influyen en el comportamiento de un par de divisas, más difícil será evaluar su comportamiento y hacer previsiones. Por lo tanto, si conseguimos extraer de los componentes de un par de divisas los valores de una divisa local en función del tiempo, podremos reducir significativamente el movimiento de la divisa local en comparación con el par de divisas que contiene esta divisa, además del número de factores que influyen en su comportamiento. Por consiguiente, mejoramos la precisión de la evaluación de su comportamiento y la predicción de sus valores. ¿Cómo podemos hacerlo?
Interacción entre MetaTrader 4 y MATLAB Engine (Máquina virtual MATLAB) Interacción entre MetaTrader 4 y MATLAB Engine (Máquina virtual MATLAB)
El artículo contiene las consideraciones en relación a la creación de una librería DLL: el envase que habilitará la interacción de MetaTrader 4 y el paquete de escritorio matemático de MATLAB. Describe los errores y las maneras de resolverlos. Este artículo está destinado a programadores preparados en C/C++ que utilizan el compilador Borland C++ Builder 6.