
Interfaces gráficas V: Barra de desplazamiento vertical y horizontal (Capítulo 1)
Índice
- Introducción
- Control “Barra de desplazamiento”
- Clase base del control
- Clases derivadas del control
- Conclusión
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.
- Fondo principal.
- Fondo del área de desplazamiento del deslizador.
- Dos botones de flecha para mover el bloque de datos a un paso.
- 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”.
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:
- Interfaces gráficas V: Barra de desplazamiento vertical y horizontal (Capítulo 1)
- Interfaces gráficas V: Control “Lista” (Capítulo 2)
- Interfaces gráficas V: Control “Lista combinada” (Capítulo 3)
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/2379





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso