Prioridad de los objetos (orden Z)

Los objetos en el gráfico proporcionan no sólo la presentación de la información, sino también la interacción con el usuario y los programas MQL a través de eventos, que se abordarán en detalle en el capítulo siguiente. Una de las fuentes de eventos es el puntero del ratón. El gráfico es capaz, en concreto, de seguir el movimiento del ratón y la pulsación de sus botones.

Si un objeto se encuentra bajo el ratón se puede realizar una gestión de eventos específica para él. Sin embargo, los objetos pueden solaparse entre sí (cuando sus coordenadas se solapan, teniendo en cuenta los tamaños). En este caso entra en juego la propiedad de tipo entero OBJPROP_ZORDER. Establece la prioridad del objeto gráfico para recibir eventos de ratón. Cuando los objetos se solapan, sólo uno de ellos, cuya prioridad es superior a la del resto, recibirá el evento.

De manera predeterminada, cuando se crea un objeto, su orden Z es cero, pero puede aumentarlo si es necesario.

Es importante tener en cuenta que el orden Z sólo afecta al manejo de los eventos del ratón, no al dibujo de los objetos. Los objetos se dibujan siempre en el orden en que se añadieron al gráfico. Esto puede ser fuente de malentendidos. Por ejemplo, puede que no se muestre un información sobre herramientas para un objeto que está visualmente encima de otro porque el objeto superpuesto tiene una prioridad Z más alta (ver ejemplo).

En el script ObjectZorder.mq5 crearemos 12 objetos de tipo OBJ_RECTANGLE_LABEL, colocándolos en un círculo, como en la esfera de un reloj. El orden de adición de los objetos corresponde a las horas: de 1 a 12. Para mayor claridad, todos los rectángulos tendrán un color aleatorio (para la propiedad OBJPROP_BGCOLOR, véase la sección siguiente), así como la prioridad aleatoria. Al pasar el ratón por encima de los objetos, el usuario podrá determinar a qué objeto pertenece mediante un «tooltip», o información sobre herramientas.

Para facilitar la configuración de las propiedades de los objetos, definimos la clase especial ObjectBuilder, derivada de Object Selector.

#include "ObjectPrefix.mqh"
#include <MQL5Book/ObjectMonitor.mqh>
   
class ObjectBuilderpublic ObjectSelector
{
protected:
   const ENUM_OBJECT type;
   const int window;
public:
   ObjectBuilder(const string _idconst ENUM_OBJECT _type,
      const long _chart = 0const int _win = 0):
      ObjectSelector(_id_chart), type(_type), window(_win)
   {
      ObjectCreate(hostidtypewindow00);
   }
   
   // changing the name and chart is prohibited
   virtual void name(const string _idoverride = delete;
   virtual void chart(const long _chartoverride = delete;
};

Los campos con identificadores del objeto (id) y del gráfico (host) ya están en la clase ObjectSelector. En la derivada, añadimos un tipo de objeto (ENUM_OBJECT type) y un número de ventana (int window). El constructor llama a ObjectCreate.

La configuración y lectura de propiedades se hereda completamente como un grupo de métodos get y set de ObjectSelector.

Como en los scripts de prueba anteriores, determinamos la ventana donde se suelta el script, las dimensiones de la ventana y las coordenadas del centro.

void OnStart()
{
   const int t = ChartWindowOnDropped();
   int h = (int)ChartGetInteger(0CHART_HEIGHT_IN_PIXELSt);
   int w = (int)ChartGetInteger(0CHART_WIDTH_IN_PIXELS);
   int x = w / 2;
   int y = h / 2;
   ...

Dado que el tipo de objeto OBJ_RECTANGLE_LABEL admite dimensiones de píxel explícitas, calculamos la anchura de dx y la altura de dy de cada rectángulo como un cuarto de ventana. Las utilizamos para establecer las propiedades OBJPROP_XSIZE y OBJPROP_YSIZE que se abordan en la sección sobre Determinar ancho y alto del objeto.

   const int dx = w / 4;
   const int dy = h / 4;
   ...

A continuación, en el bucle, creamos 12 objetos. Las variables px y py contienen el desplazamiento de la siguiente «marca» en el «dial» con respecto al centro (x, y). La prioridad de z se elige aleatoriamente. El nombre del objeto y su información sobre herramientas (OBJPROP_TOOLTIP) incluyen una cadena como «XX - YYY»; XX es el número de la «hora» (la posición en el dial es de 1 a 12) e YYY es la prioridad.

   for(int i = 0i < 12; ++i)
   {
      const int px = (int)(MathSin((i + 1) * 30 * M_PI / 180) * dx) - dx / 2;
      const int py = -(int)(MathCos((i + 1) * 30 * M_PI / 180) * dy) - dy / 2;
      
      const int z = rand();
      const string text = StringFormat("%02d - %d"i + 1z);
   
      ObjectBuilder *builder =
         new ObjectBuilder(ObjNamePrefix + textOBJ_RECTANGLE_LABEL);
      builder.set(OBJPROP_XDISTANCEx + px).set(OBJPROP_YDISTANCEy + py)
      .set(OBJPROP_XSIZEdx).set(OBJPROP_YSIZEdy)
      .set(OBJPROP_TOOLTIPtext)
      .set(OBJPROP_ZORDERz)
      .set(OBJPROP_BGCOLOR, (rand() << 8) | rand());
      delete builder;
   }

Después de llamar al constructor ObjectBuilder, para el nuevo objeto builder se encadenan las llamadas al método sobrecargado set para diferentes propiedades (el método set devuelve un puntero al objeto en sí).

Dado que el objeto MQL ya no es necesario tras la creación y configuración del objeto gráfico, borramos inmediatamente builder.

Como resultado de la ejecución del script, aparecerán aproximadamente los siguientes objetos en el gráfico:

Información sobre herramientas de superposición de objetos y prioridad de orden Z

Información sobre herramientas de superposición de objetos y prioridad de orden Z

Los colores y las prioridades serán diferentes cada vez que lo ejecute, pero la superposición visual de los rectángulos será siempre la misma, en el orden de creación desde el 1 en la parte inferior hasta el 12 en la parte superior (aquí nos referimos a la superposición de objetos, no al hecho de que el 12 esté situado en la parte superior de la esfera del reloj ).

En la imagen, el cursor del ratón se sitúa en un lugar en el que existen dos objetos, es decir, 01 (verde lima fluorescente) y 12 (arena). En este caso, la información sobre herramientas del objeto 01 está visible, aunque visualmente el objeto 12 se muestra encima del objeto 01. Esto se debe a que 01 se generó aleatoriamente con una prioridad mayor que 12.

Sólo se muestra una información sobre herramientas en cada momento, por lo que puede comprobar la relación de prioridad moviendo el cursor del ratón a otras zonas en las que no haya superposición de objetos y la información de la información sobre herramientas pertenezca al único objeto situado bajo el cursor.

Cuando hablemos del manejo de eventos del ratón en el próximo capítulo, podremos mejorar este ejemplo y comprobar el efecto del orden Z en los clics del ratón sobre los objetos.

Para eliminar los objetos creados, puede utilizar el script ObjectCleanup1.mq5.