DoEasy. Elementos de control (Parte 10): Objetos WinForms: dando vida a la interfaz
Contenido
Concepto
En esencia, todos los objetos WinForms que hemos creado anteriormente son imágenes estáticas, meros "accesorios" que pueden moverse por la pantalla. Esto tiene muy poco sentido, y es hora de empezar a revitalizar la interfaz gráfica de usuario, haciendo que los objetos puedan interactuar con el usuario y con otros objetos.
Los objetos WinForms que ya hemos creado suponen tanto unidades independientes para construir una interfaz gráfica, como bloques de construcción para objetos más complejos. Y para que los objetos más complejos funcionen correctamente, necesitaremos una funcionalidad que permita a los objetos interactuar entre sí y con el usuario. Hoy vamos a poner en práctica esto y a crear un componente visual cuando los objetos interactúan con el ratón. En futuros artículos, implementaremos funcionalidades basadas en eventos para estos objetos, y continuaremos creando nuevos objetos de la biblioteca WinForms.
Todo objeto de GUI activo para el que se haya definido la posibilidad de interactuar con el ratón y que tenga una bandera de accesibilidad deberá indicar al usuario que es posible interactuar con él. Por ejemplo, un botón sencillo. Al pasar el ratón por encima, el botón cambiará ligeramente de color, indicando al usuario que está activo y listo para interactuar. Cuando lo pulsemos, volverá a cambiar de color, pero no se activará aún, se disparará cuando pulsemos el botón del ratón dentro del objeto de interacción. Si, por el contrario, pulsamos el botón del ratón sobre un objeto y, sin soltarlo, alejamos el cursor del objeto para a continuación soltar el botón del ratón, el objeto no debería activarse: volverá a su estado original, antes de que se pulsara el botón del ratón sobre él. Así se comportan todos los objetos del sistema operativo Windows. Aquí también implementaremos el mismo comportamiento de los objetos WinForms.
Después de crear el componente visual, empezaremos a crear objetos WinForms controlados por eventos, lo cual nos permitirá crear objetos más complejos a partir de otros simples. Los eventos de los objetos simples que componen el objeto complejo serán procesados por el propio objeto; una vez analizados, su apariencia cambiará en consecuencia, y como resultado el objeto enviará el evento ocurrido en dicho objeto al gráfico del programa de control.
Como el objeto necesitará ahora varios colores más para el mismo estado (color básico, color al colocar el cursor del ratón, color al pulsar el botón sobre el objeto), vamos a escribir estos nuevos colores para las diferentes propiedades del objeto que puedan tenerlos. Si los nuevos colores están presentes en las propiedades de los objetos, nos resultará más sencillo crear editores visuales para estos objetos más adelante: todas las propiedades podrán mostrarse en un panel y luego gestionarse. Si no especificamos los nuevos colores complementarios en las propiedades del objeto, tendremos que indicarlos manualmente para cada objeto para su muestra, lo cual no resulta en absoluto óptimo.
Mejorando las clases de la biblioteca
En el archivo \MQL5\Include\DoEasy\Defines.mqh , escribimos las nuevas macros para los colores por defecto de los diferentes estados de los objetos WinForms:
//--- Canvas parameters #define PAUSE_FOR_CANV_UPDATE (16) // Canvas update frequency #define CLR_CANV_NULL (0x00FFFFFF) // Zero for the canvas with the alpha channel #define CLR_DEF_FORE_COLOR (C'0x2D,0x43,0x48') // Default color for texts of objects on canvas #define CLR_DEF_FORE_COLOR_MOUSE_DOWN (C'0x0E,0x11,0x98') // Default color for texts of objects on canvas when clicking the mouse on the control #define CLR_DEF_FORE_COLOR_MOUSE_OVER (C'0x14,0x67,0xF1') // Default color for texts of objects on canvas when hovering the mouse over the control #define CLR_DEF_FORE_COLOR_OPACITY (255) // Default color non-transparency for canvas object texts #define CLR_DEF_BORDER_COLOR (C'0x2D,0x43,0x48') // Default color for object frames on canvas #define CLR_DEF_BORDER_MOUSE_DOWN (C'0x61,0x88,0xC9') // Default color for object frames on canvas when clicking the mouse on the control #define CLR_DEF_BORDER_MOUSE_OVER (C'0x93,0xAD,0xC8') // Default color for object frames on canvas when hovering the mouse over the control #define CLR_DEF_BORDER_COLOR_OPACITY (255) // Default color non-transparency for canvas object frames #define CLR_DEF_BORDER_COLOR_DARKNESS (-2.0) // Default color opacity for canvas object frames (when using the background color) #define CLR_DEF_FRAME_GBOX_COLOR (C'0xDC,0xDC,0xDC') // Default color for GroupBox object frames on canvas #define CLR_DEF_OPACITY (200) // Default color non-transparency for canvas objects #define CLR_DEF_SHADOW_COLOR (C'0x6B,0x6B,0x6B') // Default color for canvas object shadows #define CLR_DEF_SHADOW_OPACITY (127) // Default color opacity for canvas objects #define DEF_SHADOW_BLUR (4) // Default blur for canvas object shadows #define CLR_DEF_CHECK_BACK_COLOR (C'0xFF,0xFF,0xFF') // Color of control checkbox background #define CLR_DEF_CHECK_BACK_OPACITY (255) // Opacity of the control checkbox background color #define CLR_DEF_CHECK_BACK_MOUSE_DOWN (C'0xC0,0xDC,0xF3') // Color of control checkbox background when clicking on the control #define CLR_DEF_CHECK_BACK_MOUSE_OVER (C'0xD8,0xE6,0xF2') // Color of control checkbox background when hovering the mouse over the control #define CLR_DEF_CHECK_BORDER_COLOR (C'0x2D,0x43,0x48') // Color of control checkbox frame #define CLR_DEF_CHECK_BORDER_OPACITY (255) // Opacity of the control checkbox frame color #define CLR_DEF_CHECK_BORDER_MOUSE_DOWN (C'0x00,0x54,0x99') // Color of control checkbox frame when clicking on the control #define CLR_DEF_CHECK_BORDER_MOUSE_OVER (C'0x00,0x78,0xD7') // Color of control checkbox frame when hovering the mouse over the control #define CLR_DEF_CHECK_FLAG_COLOR (C'0x04,0x7B,0x0D') // Color of control checkbox #define CLR_DEF_CHECK_FLAG_OPACITY (255) // Opacity of the control checkbox color #define CLR_DEF_CHECK_FLAG_MOUSE_DOWN (C'0x00,0x54,0x99') // Color of control checkbox when clicking on the control #define CLR_DEF_CHECK_FLAG_MOUSE_OVER (C'0x00,0x78,0xD7') // Color of control checkbox when hovering the mouse over the control #define CLR_DEF_CONTROL_STD_BACK_COLOR (C'0xF0,0xF0,0xF0') // Standard controls background color #define CLR_DEF_CONTROL_STD_MOUSE_DOWN (C'0xC0,0xDC,0xF3') // Color of standard control background when clicking on the control #define CLR_DEF_CONTROL_STD_MOUSE_OVER (C'0xD8,0xE6,0xF2') // Color of standard controls background when hovering the mouse over the control #define CLR_DEF_CONTROL_STD_OPACITY (255) // Opacity of standard controls background color #define CLR_DEF_CONTROL_STD_BACK_COLOR_ON (C'0xC9,0xDE,0xD0') // Background color of standard controls which are on #define CLR_DEF_CONTROL_STD_BACK_DOWN_ON (C'0xA6,0xC8,0xB0') // Color of standard control background when clicking on the control when it is on #define CLR_DEF_CONTROL_STD_BACK_OVER_ON (C'0xB8,0xD3,0xC0') // Color of standard control background when hovering the mouse over the control when it is on #define DEF_FONT ("Calibri") // Default font #define DEF_FONT_SIZE (8) // Default font size #define DEF_CHECK_SIZE (12) // Verification flag default size #define OUTER_AREA_SIZE (16) // Size of one side of the outer area around the form workspace #define DEF_FRAME_WIDTH_SIZE (3) // Default form/panel/window frame width //--- Graphical object parameters
Después de todo, ForeColor no será necesario para la casilla de verificación. Por lo tanto, lo sustituiremos por BorderColor, el color del marco de la casilla de verificación. Esto cambiará junto con el color de fondo al darse la interacción con el ratón. Aquí utilizaremos el color de la casilla como ForeColour.
La biblioteca monitoreará el otro estado del ratón en relación con el formulario: cuando el cursor está en el área activa y el botón del ratón se ha soltado después de la pulsación. Vamos a añadir ahora el nuevo estado a la lista de posibles estados del ratón en relación con el formulario:
//+------------------------------------------------------------------+ //| The list of possible mouse states relative to the form | //+------------------------------------------------------------------+ enum ENUM_MOUSE_FORM_STATE { MOUSE_FORM_STATE_NONE = 0, // Undefined state //--- Outside the form MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED, // The cursor is outside the form, the mouse buttons are not clicked MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED, // The cursor is outside the form, the mouse button (any) is clicked MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL, // The cursor is outside the form, the mouse wheel is being scrolled //--- Within the form MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED, // The cursor is inside the form, no mouse buttons are clicked MOUSE_FORM_STATE_INSIDE_FORM_PRESSED, // The cursor is inside the form, the mouse button (any) is clicked MOUSE_FORM_STATE_INSIDE_FORM_WHEEL, // The cursor is inside the form, the mouse wheel is being scrolled //--- Within the window header area MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED, // The cursor is inside the active area, the mouse buttons are not clicked MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED, // The cursor is inside the active area, any mouse button is clicked MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL, // The cursor is inside the active area, the mouse wheel is being scrolled MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED, // The cursor is inside the active area, left mouse button is released //--- Within the window scrolling area MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED, // The cursor is within the window scrolling area, the mouse buttons are not clicked MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED, // The cursor is within the window scrolling area, the mouse button (any) is clicked MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL, // The cursor is within the window scrolling area, the mouse wheel is being scrolled }; //+------------------------------------------------------------------+
Al monitorear este estado, podremos determinar cuándo se ha soltado el botón del ratón, y este será el momento clave para cambiar el estado del objeto gráfico.
Para reaccionar a las interacciones del ratón, necesitaremos saber qué evento del ratón se ha producido. Posteriormente, basándonos en el procesamiento de dicho evento, enviaremos este al programa, para que el usuario de la biblioteca pueda procesar el evento en su programa.
A continuación, crearemos una nueva enumeración con la lista de eventos de ratón:
//+------------------------------------------------------------------+ //| List of possible mouse events | //+------------------------------------------------------------------+ enum ENUM_MOUSE_EVENT { MOUSE_EVENT_NO_EVENT = CHART_OBJ_EVENTS_NEXT_CODE, // No event //--- MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED, // The cursor is outside the form, the mouse buttons are not clicked MOUSE_EVENT_OUTSIDE_FORM_PRESSED, // The cursor is outside the form, the mouse button (any) is clicked MOUSE_EVENT_OUTSIDE_FORM_WHEEL, // The cursor is outside the form, the mouse wheel is being scrolled //--- Within the form MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED, // The cursor is inside the form, no mouse buttons are clicked MOUSE_EVENT_INSIDE_FORM_PRESSED, // The cursor is inside the form, the mouse button (any) is clicked MOUSE_EVENT_INSIDE_FORM_WHEEL, // The cursor is inside the form, the mouse wheel is being scrolled //--- Within the window header area MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED, // The cursor is inside the active area, the mouse buttons are not clicked MOUSE_EVENT_INSIDE_ACTIVE_AREA_PRESSED, // The cursor is inside the active area, any mouse button is clicked MOUSE_EVENT_INSIDE_ACTIVE_AREA_WHEEL, // The cursor is inside the active area, the mouse wheel is being scrolled MOUSE_EVENT_INSIDE_ACTIVE_AREA_RELEASED, // The cursor is inside the active area, left mouse button is released //--- Within the window scrolling area MOUSE_EVENT_INSIDE_SCROLL_AREA_NOT_PRESSED, // The cursor is within the window scrolling area, the mouse buttons are not clicked MOUSE_EVENT_INSIDE_SCROLL_AREA_PRESSED, // The cursor is within the window scrolling area, the mouse button (any) is clicked MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL, // The cursor is within the window scrolling area, the mouse wheel is being scrolled }; #define ENUM_MOUSE_EVENT_NEXT_CODE (MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL+1) // The code of the next event after the last chart event code //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Data for handling graphical elements | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of possible graphical object events | //+------------------------------------------------------------------+ enum ENUM_GRAPH_OBJ_EVENT { GRAPH_OBJ_EVENT_NO_EVENT = ENUM_MOUSE_EVENT_NEXT_CODE,// No event GRAPH_OBJ_EVENT_CREATE, // "Creating a new graphical object" event GRAPH_OBJ_EVENT_CHANGE, // "Changing graphical object properties" event GRAPH_OBJ_EVENT_RENAME, // "Renaming graphical object" event GRAPH_OBJ_EVENT_DELETE, // "Removing graphical object" event GRAPH_OBJ_EVENT_DEL_CHART, // "Removing a graphical object together with the chart window" event }; #define GRAPH_OBJ_EVENTS_NEXT_CODE (GRAPH_OBJ_EVENT_DEL_CHART+1) // The code of the next event after the last graphical object event code //+------------------------------------------------------------------+
Como hemos insertado la nueva enumeración de eventos "embutiéndola" entre la lista de eventos del gráfico y la lista de eventos del objeto gráfico, el valor inicial del primer evento de los objetos gráficos se calculará ahora como el último evento de ratón+1.
En la lista de propiedades enteras del elemento gráfico añadiremos las nuevas propiedades:
//+------------------------------------------------------------------+ //| Integer properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_INTEGER { CANV_ELEMENT_PROP_ID = 0, // Element ID CANV_ELEMENT_PROP_TYPE, // Graphical element type //---... //---... CANV_ELEMENT_PROP_FORE_COLOR, // Default text color for all control objects CANV_ELEMENT_PROP_FORE_COLOR_OPACITY, // Default text color opacity for all control objects CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN, // Default control text color when clicking on the control CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER, // Default control text color when hovering the mouse over the control CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE, // Text color of the control which is on CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN, // Default control text color when clicking on the control which is on CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER, // Default control text color when hovering the mouse over the control which is on CANV_ELEMENT_PROP_BACKGROUND_COLOR, // Control background color CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY, // Opacity of control background color CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN, // Control background color when clicking on the control CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER, // Control background color when hovering the mouse over the control CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE, // Background color of the control which is on CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,// Control background color when clicking on the control which is on CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,// Control background color hovering the mouse over control which is on CANV_ELEMENT_PROP_BOLD_TYPE, // Font width type CANV_ELEMENT_PROP_BORDER_STYLE, // Control frame style //---... //---... CANV_ELEMENT_PROP_CHECK_STATE, // Status of a control having a checkbox CANV_ELEMENT_PROP_AUTOCHECK, // Auto change flag status when it is selected CANV_ELEMENT_PROP_BUTTON_TOGGLE, // Toggle flag of the control featuring a button CANV_ELEMENT_PROP_BUTTON_STATE, // Status of the Toggle control featuring a button CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR, // Color of control checkbox background CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY, // Opacity of the control checkbox background color //---... //---... CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN, // Color of control checkbox when clicking on the control CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER, // Color of control checkbox when hovering the mouse over the control }; #define CANV_ELEMENT_PROP_INTEGER_TOTAL (81) // Total number of integer properties #define CANV_ELEMENT_PROP_INTEGER_SKIP (0) // Number of integer properties not used in sorting //+------------------------------------------------------------------+
y aumentaremos el número total de propiedades de 71 a 81.
Vamos a añadir a la enumeración de posibles criterios de clasificación de los objetos gráficos en el lienzo estas nuevas propiedades:
//+------------------------------------------------------------------+ //| Possible sorting criteria of graphical elements on the canvas | //+------------------------------------------------------------------+ #define FIRST_CANV_ELEMENT_DBL_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP) #define FIRST_CANV_ELEMENT_STR_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP) enum ENUM_SORT_CANV_ELEMENT_MODE { //--- Sort by integer properties SORT_BY_CANV_ELEMENT_ID = 0, // Sort by element ID SORT_BY_CANV_ELEMENT_TYPE, // Sort by graphical element type //---... //---... SORT_BY_CANV_ELEMENT_FORE_COLOR, // Sort by default text color for all control objects SORT_BY_CANV_ELEMENT_FORE_COLOR_OPACITY, // Sort by default text color opacity for all control objects SORT_BY_CANV_ELEMENT_FORE_COLOR_MOUSE_DOWN, // Sort by control text color when clicking on the control SORT_BY_CANV_ELEMENT_FORE_COLOR_MOUSE_OVER, // Sort by control text color when hovering the mouse over the control SORT_BY_CANV_ELEMENT_FORE_COLOR_TOGGLE, // Sort by control text color when the control is on SORT_BY_CANV_ELEMENT_FORE_COLOR_TOGGLE_MOUSE_DOWN, // Sort by the default control text color when clicking on the control while it is on SORT_BY_CANV_ELEMENT_FORE_COLOR_TOGGLE_MOUSE_OVER, // Sort by the default control text color when hovering the mouse over the control while it is on SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR, // Sort by control background text color SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_OPACITY, // Sort by control background color opacity SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_MOUSE_DOWN, // Sort by control background text color when clicking on the control SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_MOUSE_OVER, // Sort by control background text color when hovering the mouse over the control SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_TOGGLE, // Sort by control background color when the control is on SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,// Sort by control background color when clicking on the control while it is on SORT_BY_CANV_ELEMENT_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,// Sort by control background color when hovering the mouse over the control while it is on SORT_BY_CANV_ELEMENT_BOLD_TYPE, // Sort by font width type SORT_BY_CANV_ELEMENT_BORDER_STYLE, // Sort by control frame style //---... //---... SORT_BY_CANV_ELEMENT_CHECK_STATE, // Sort by status of a control having a checkbox SORT_BY_CANV_ELEMENT_AUTOCHECK, // Sort by auto change flag status when it is selected SORT_BY_CANV_ELEMENT_BUTTON_TOGGLE, // Sort by the Toggle flag of the control featuring a button SORT_BY_CANV_ELEMENT_BUTTON_STATE, // Sort by the status of the Toggle control featuring a button SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR, // Sort by color of control checkbox background SORT_BY_CANV_ELEMENT_CHECK_BACKGROUND_COLOR_OPACITY, // Sort by opacity of control checkbox background color //---... //---... SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_DOWN, // Sort by color of control checkbox when clicking on the control SORT_BY_CANV_ELEMENT_CHECK_FLAG_COLOR_MOUSE_OVER, // Sort by color of control checkbox when hovering the mouse over the control //--- Sort by real properties //--- Sort by string properties SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by an element object name SORT_BY_CANV_ELEMENT_NAME_RES, // Sort by the graphical resource name SORT_BY_CANV_ELEMENT_TEXT, // Sort by graphical element text }; //+------------------------------------------------------------------+
Ahora podremos clasificar y filtrar las listas, y también elegir los objetos según estas nuevas propiedades.
En el archivo \MQL5\Include\DoEasy\Data.mqh, añadimos los índices de los nuevos mensajes a la biblioteca:
MSG_CANV_ELEMENT_PROP_FORE_COLOR, // Default text color for all control objects MSG_CANV_ELEMENT_PROP_FORE_COLOR_OPACITY, // Default text color opacity for all control objects MSG_CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN, // Default control text color when clicking on the control MSG_CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER, // Default control text color when hovering the mouse over the control MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE, // Text color of the control which is on MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN,// Default control text color when clicking on the control which is on MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER,// Default control text color when hovering the mouse over the control which is on MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR, // Control background color MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_OPACITY, // Opacity of control background color MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN, // Control background color when clicking on the control MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER, // Control background color when hovering the mouse over the control MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE, // Background color of the control which is on MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,// Control background color when clicking on the control which is on MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,// Control background color when hovering the mouse over control which is on MSG_CANV_ELEMENT_PROP_BOLD_TYPE, // Font width type MSG_CANV_ELEMENT_PROP_BORDER_STYLE, // Control frame style
...
MSG_CANV_ELEMENT_PROP_CHECK_STATE, // Status of a control having a checkbox MSG_CANV_ELEMENT_PROP_AUTOCHECK, // Auto change flag status when it is selected MSG_CANV_ELEMENT_PROP_BUTTON_TOGGLE, // Toggle flag of the control featuring a button MSG_CANV_ELEMENT_PROP_BUTTON_STATE, // Status of the Toggle control featuring a button MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR, // Color of control checkbox background MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,// Opacity of the control checkbox background color MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,// Color of control checkbox background when clicking on the control MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,// Color of control checkbox background when hovering the mouse over the control MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR, // Color of control checkbox frame MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY, // Opacity of the control checkbox frame color MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN, // Color of control checkbox frame when clicking on the control MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER, // Color of control checkbox frame when hovering the mouse over the control MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR, // Color of control checkbox MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY, // Opacity of the control checkbox color MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN, // Color of control checkbox when clicking on the control MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER, // Color of control checkbox when hovering the mouse over the control //--- Real properties of graphical elements //--- String properties of graphical elements MSG_CANV_ELEMENT_PROP_NAME_OBJ, // Graphical element object name MSG_CANV_ELEMENT_PROP_NAME_RES, // Graphical resource name MSG_CANV_ELEMENT_PROP_TEXT, // Graphical element text
y los textos de los mensajes correspondientes a los nuevos índices añadidos:
{"Цвет текста по умолчанию для всех объектов элемента управления","Default text color for all objects in the control"}, {"Непрозрачность цвета текста по умолчанию для всех объектов элемента управления","Default text color opacity for all objects in the control"}, {"Цвет текста элемента по умолчанию при нажатии мышки на элемент управления","The default text color of an element when the mouse is pressed on the control"}, {"Цвет текста элемента по умолчанию при наведении мышки на элемент управления","The default text color of an element when hovering over the control"}, {"Цвет текста элемента управления в состоянии \"включено\"","The text color of a control in the enabled state"}, {"Цвет текста элемента управления по умолчанию в состоянии \"включено\" при нажатии мышки на элемент управления","The default text color of the control in the \"On\" state when the mouse is pressed on the control"}, {"Цвет текста элемента управления по умолчанию в состоянии \"включено\" при наведении мышки на элемент управления","The default text color of a control in the \"On\" state when hovering the mouse over the control"}, {"Цвет фона элемента управления","Background color of the control"}, {"Непрозрачность цвета фона элемента управления","Opacity of the control's background color"}, {"Цвет фона элемента управления при нажатии мышки на элемент управления","Background color of the control when the mouse is clicked on the control"}, {"Цвет фона элемента управления при наведении мышки на элемент управления","Background color of the control when hovering the mouse over the control"}, {"Цвет фона элемента управления в состоянии \"включено\"","Background color of the control in the enabled state"}, {"Цвет фона элемента управления в состоянии \"включено\" при нажатии мышки на элемент управления","The background color of the control in the \"On\" state when the mouse is pressed on the control"}, {"Цвет фона элемента управления в состоянии \"включено\" при наведении мышки на элемент управления","The background color of a control in the \"On\" state when the mouse is over the control"}, {"Тип толщины шрифта","Font weight type"}, {"Стиль рамки элемента управления","Control's border style"},
...
{"Состояние элемента управления, имеющего флажок проверки","The state of a control that has a checkbox"}, {"Автоматическое изменение состояния флажка при его выборе","Automatically change the state of the checkbox when it is selected"}, {"Флаг \"Переключатель\" элемента управления, имеющего кнопку","\"Button-toggle\" flag of a control"}, {"Состояние элемента управления \"Переключатель\", имеющего кнопку","The \"Toggle-button\" control state"}, {"Цвет фона флажка проверки элемента управления","The background color of the control's validation checkbox"}, {"Непрозрачность цвета фона флажка проверки элемента управления","Opacity of the backgroung color of the checkbox control"}, {"Цвет фона флажка проверки элемента управления при нажатии мышки на элемент управления","The background color of the control's checkbox when the control is pressed with the mouse"}, {"Цвет фона флажка проверки элемента управления при наведении мышки на элемент управления","The background color of the control's validation checkbox when hovering the mouse over the control"}, {"Цвет рамки флажка проверки элемента управления","Border color of the checkbox control"}, {"Непрозрачность цвета рамки флажка проверки элемента управления","Border color opacity of the checkbox control"}, {"Цвет рамки флажка проверки элемента управления при нажатии мышки на элемент управления","Border color of the checkbox control when the mouse is pressed on the control"}, {"Цвет рамки флажка проверки элемента управления при наведении мышки на элемент управления","Border color of the checkbox control when hovering the mouse over the control"}, {"Цвет флажка проверки элемента управления","Control Checkbox Color"}, {"Непрозрачность цвета флажка проверки элемента управления","Opacity of control's checkbox color"}, {"Цвет флажка проверки элемента управления при нажатии мышки на элемент управления","Control Checkbox Colorl when the mouse is pressed on the control"}, {"Цвет флажка проверки элемента управления при наведении мышки на элемент управления","Control Checkbox Colorl when hovering the mouse over the control"}, //--- String properties of graphical elements {"Имя объекта-графического элемента","The name of the graphic element object"}, {"Имя графического ресурса","Image resource name"}, {"Текст графического элемента","Text of the graphic element"},
Al pasar el ratón por encima de un objeto o clicar en él, tendremos que cambiar su color de fondo. Pero para restablecer el color original, hay que memorizarlo la primera vez que se construye un objeto, y luego tomar el color necesario del color anteriormente almacenado.
Como el color de fondo del objeto gráfico puede ser un color de gradiente, es decir, se puede utilizar un array de colores, necesitaremos no solo una variable para almacenar el color original, sino un array. Este se rellenará exactamente de la misma forma que se rellena el array de colores de gradiente al construir un objeto. Al restaurar un color, tomaremos el color del array guardado.
En el archivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, en su sección protegida, declaramos el array de colores de fondo iniciales y trasladamos aquí desde la clase privada el método para rellenar las matrices de colores: lo necesitaremos en las clases herederas:
//+------------------------------------------------------------------+ //| Class of the graphical element object | //+------------------------------------------------------------------+ class CGCnvElement : public CGBaseObj { protected: CGCnvElement *m_element_main; // Pointer to the initial parent element within all the groups of bound objects CGCnvElement *m_element_base; // Pointer to the parent element within related objects of the current group CCanvas m_canvas; // CCanvas class object CPause m_pause; // Pause class object bool m_shadow; // Shadow presence color m_chart_color_bg; // Chart background color uint m_duplicate_res[]; // Array for storing resource data copy color m_array_colors_bg[]; // Array of element background colors color m_array_colors_bg_dwn[]; // Array of control background colors when clicking on the control color m_array_colors_bg_ovr[]; // Array of control background colors when hovering the mouse over the control bool m_gradient_v; // Vertical gradient filling flag bool m_gradient_c; // Cyclic gradient filling flag int m_init_relative_x; // Initial relative X coordinate int m_init_relative_y; // Initial relative Y coordinate color m_array_colors_bg_init[]; // Array of element background colors (initial color) //--- Create (1) the object structure and (2) the object from the structure virtual bool ObjectToStruct(void); virtual void StructToObject(void); //--- Copy the color array to the specified background color array void CopyArraysColors(color &array_dst[],const color &array_src[],const string source); private:
En la sección privada de la clase, en la estructura de datos del objeto, escribiremos las nuevas propiedades enteras del objeto:
private: int m_shift_coord_x; // Offset of the X coordinate relative to the base object int m_shift_coord_y; // Offset of the Y coordinate relative to the base object struct SData { //--- Object integer properties int id; // Element ID int type; // Graphical element type //---... //---... color check_flag_color_mouse_down; // Color of control checkbox when clicking on the control color check_flag_color_mouse_over; // Color of control checkbox when clicking on the control color fore_color_mouse_down; // Default control text color when clicking on the control color fore_color_mouse_over; // Default control text color when hovering the mouse over the control color fore_color_toggle; // Text color of the control which is on color fore_color_toggle_mouse_down; // Default control text color when clicking on the control which is on color fore_color_toggle_mouse_over; // Default control text color when hovering the mouse over the control which is on color background_color_toggle; // Background color of the control which is on color background_color_toggle_mouse_down; // Control background color when clicking on the control which is on color background_color_toggle_mouse_over; // Control background color when hovering the mouse over the control which is on bool button_toggle; // Toggle flag of the control featuring a button bool button_state; // Status of the Toggle control featuring a button //--- Object real properties //--- Object string properties uchar name_obj[64]; // Graphical element object name uchar name_res[64]; // Graphical resource name uchar text[256]; // Graphical element text }; SData m_struct_obj; // Object structure uchar m_uchar_array[]; // uchar array of the object structure
Esto será necesario para garantizar que el objeto se guarde correctamente en el soporte y se lea de él en el futuro.
Vamos a escribir otro método para almacenar un array de colores de gradiente para recordar el color de fondo original:
//--- Save the colors to the background color array void SaveColorsBG(color &colors[]) { this.CopyArraysColors(this.m_array_colors_bg,colors,DFUN); } void SaveColorsBGMouseDown(color &colors[]) { this.CopyArraysColors(this.m_array_colors_bg_dwn,colors,DFUN); } void SaveColorsBGMouseOver(color &colors[]) { this.CopyArraysColors(this.m_array_colors_bg_ovr,colors,DFUN); } void SaveColorsBGInit(color &colors[]) { this.CopyArraysColors(this.m_array_colors_bg_init,colors,DFUN); } public:
El método se llamará en el método que establece el color de fondo. Para ello, introduciremos en sus variables formales una bandera indicando que se debe mantener el color de fondo original después de establecer el color de fondo del objeto. De esta forma, podremos elegir: o bien establecemos el color de fondo del objeto y almacenamos sus colores originales en un array, o simplemente establecemos un nuevo color de fondo que luego podremos restaurar a partir de todo el conjunto de colores de gradiente de fondo previamente escritos en el array. Haremos esto para todos los objetos cuyo color deba cambiar cuando interactúan con el ratón:
//--- Set the main background color void SetBackgroundColor(const color colour,const bool set_init_color) { this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,colour); color arr[1]; arr[0]=colour; this.SaveColorsBG(arr); if(set_init_color) this.SetBackgroundColorInit(this.BackgroundColor()); } void SetBackgroundColors(color &colors[],const bool set_init_colors) { this.SaveColorsBG(colors); this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR,this.m_array_colors_bg[0]); if(set_init_colors) this.SetBackgroundColorsInit(colors); }
Vamos a escribir métodos para establecer el color de fondo inicial idénticos a los anteriormente analizados:
void SetBackgroundColorsMouseOver(color &colors[]) { this.SaveColorsBGMouseOver(colors); this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.m_array_colors_bg_ovr[0]); } //--- Set the initial main background color void SetBackgroundColorInit(const color colour) { color arr[1]; arr[0]=colour; this.SaveColorsBGInit(arr); } void SetBackgroundColorsInit(color &colors[]) { this.SaveColorsBGInit(colors); } //--- Set (1) object movability, (2) activity, (3) interaction,
Aquí todo será igual que en los métodos para establecer el color de fondo básico, salvo por la bandera que indica que debemos guardar el color original, que es exactamente lo que guardamos al llamar a estos métodos.
Vamos a escribir métodos que retornan el color de fondo original:
//--- Return the background color when hovering the mouse over the control color BackgroundColorMouseOver(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER); } color BackgroundColorMouseOver(const uint index) const { uint total=this.m_array_colors_bg_ovr.Size(); if(total==0) return this.BackgroundColorMouseOver(); return(index>total-1 ? this.m_array_colors_bg_ovr[total-1] : this.m_array_colors_bg_ovr[index]); } //--- Return the initial color of the main background color BackgroundColorInit(void) const { return (color)this.m_array_colors_bg_init[0]; } color BackgroundColorInit(const uint index)const { uint total=this.m_array_colors_bg_init.Size(); if(total==0) return this.BackgroundColor(); return(index>total-1 ? this.m_array_colors_bg_init[total-1] : this.m_array_colors_bg_init[index]); } //--- Return (1) the opacity, coordinate (2) of the right and (3) bottom element edge
Los métodos retornan un único color de fondo o un color formado por el conjunto de colores de gradiente correspondiente al índice transmitido al método.
En ambos constructores de la clase, inicializamos las nuevas propiedades y transmitimos la bandera para guardar el color de fondo original:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int element_id, const int element_num, const long chart_id, const int wnd_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable=true, const bool activity=true, const bool redraw=false) : m_shadow(false) { this.m_type=OBJECT_DE_TYPE_GELEMENT; this.m_element_main=NULL; this.m_element_base=NULL; this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND); this.m_name=(::StringFind(name,this.m_name_prefix)<0 ? this.m_name_prefix : "")+name; this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id); this.m_subwindow=wnd_num; this.m_type_element=element_type; this.SetFont(DEF_FONT,DEF_FONT_SIZE); this.m_text_anchor=0; this.m_text_x=0; this.m_text_y=0; this.SetBackgroundColor(colour,true); this.SetOpacity(opacity); this.m_shift_coord_x=0; this.m_shift_coord_y=0; if(::ArrayResize(this.m_array_colors_bg,1)==1) this.m_array_colors_bg[0]=this.BackgroundColor(); if(::ArrayResize(this.m_array_colors_bg_dwn,1)==1) this.m_array_colors_bg_dwn[0]=this.BackgroundColor(); if(::ArrayResize(this.m_array_colors_bg_ovr,1)==1) this.m_array_colors_bg_ovr[0]=this.BackgroundColor(); if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,redraw)) { this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID()); // Chart ID this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow()); // Chart subwindow index this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name()); // Element object name this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type); // Graphical element type this.SetProperty(CANV_ELEMENT_PROP_ID,element_id); // Element ID this.SetProperty(CANV_ELEMENT_PROP_NUM,element_num); // Element index in the list this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x); // Element's X coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y); // Element's Y coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w); // Element width this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h); // Element height this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0); // Active area offset from the left edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0); // Active area offset from the upper edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0); // Active area offset from the right edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0); // Active area offset from the bottom edge of the element this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable); // Element moveability flag this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,activity); // Element activity flag this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,false); // Flag of interaction with the outside environment this.SetProperty(CANV_ELEMENT_PROP_ENABLED,true); // Element availability flag this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge()); // Element right border this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge()); // Element bottom border this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft()); // X coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop()); // Y coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight()); // Right border of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom()); // Bottom border of the element active area //--- this.SetProperty(CANV_ELEMENT_PROP_BELONG,ENUM_GRAPH_OBJ_BELONG::GRAPH_OBJ_BELONG_PROGRAM); // Graphical element affiliation this.SetProperty(CANV_ELEMENT_PROP_ZORDER,0); // Priority of a graphical object for receiving the event of clicking on a chart this.SetProperty(CANV_ELEMENT_PROP_BOLD_TYPE,FW_NORMAL); // Font width type this.SetProperty(CANV_ELEMENT_PROP_BORDER_STYLE,FRAME_STYLE_NONE); // Control frame style this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP,0); // Control frame top size this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,0); // Control frame bottom size this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,0); // Control frame left size this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,0); // Control frame right size this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR,this.BackgroundColor()); // Control frame color this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE,false); // Flag of the element auto resizing depending on the content this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE,CANV_ELEMENT_AUTO_SIZE_MODE_GROW); // Mode of the element auto resizing depending on the content this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL,false); // Auto scrollbar flag this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_W,0); // Width of the field inside the element during auto scrolling this.SetProperty(CANV_ELEMENT_PROP_AUTOSCROLL_MARGIN_H,0); // Height of the field inside the element during auto scrolling this.SetProperty(CANV_ELEMENT_PROP_DOCK_MODE,CANV_ELEMENT_DOCK_MODE_NONE); // Mode of binding control borders to the container this.SetProperty(CANV_ELEMENT_PROP_MARGIN_TOP,0); // Top margin between the fields of this and another control this.SetProperty(CANV_ELEMENT_PROP_MARGIN_BOTTOM,0); // Bottom margin between the fields of this and another control this.SetProperty(CANV_ELEMENT_PROP_MARGIN_LEFT,0); // Left margin between the fields of this and another control this.SetProperty(CANV_ELEMENT_PROP_MARGIN_RIGHT,0); // Right margin between the fields of this and another control this.SetProperty(CANV_ELEMENT_PROP_PADDING_TOP,0); // Top margin inside the control this.SetProperty(CANV_ELEMENT_PROP_PADDING_BOTTOM,0); // Bottom margin inside the control this.SetProperty(CANV_ELEMENT_PROP_PADDING_LEFT,0); // Left margin inside the control this.SetProperty(CANV_ELEMENT_PROP_PADDING_RIGHT,0); // Right margin inside the control this.SetProperty(CANV_ELEMENT_PROP_TEXT_ALIGN,ANCHOR_LEFT_UPPER); // Text position within text label boundaries this.SetProperty(CANV_ELEMENT_PROP_CHECK_ALIGN,ANCHOR_LEFT_UPPER); // Position of the checkbox within control borders this.SetProperty(CANV_ELEMENT_PROP_CHECKED,false); // Control checkbox status this.SetProperty(CANV_ELEMENT_PROP_CHECK_STATE,CANV_ELEMENT_CHEK_STATE_UNCHECKED); // Status of a control having a checkbox this.SetProperty(CANV_ELEMENT_PROP_AUTOCHECK,true); // Auto change flag status when it is selected //--- this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,CLR_DEF_CHECK_BACK_COLOR); // Color of control checkbox background this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,CLR_DEF_CHECK_BACK_OPACITY); // Opacity of the control checkbox background color this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BACK_MOUSE_DOWN);// Color of control checkbox background when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BACK_MOUSE_OVER);// Color of control checkbox background when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,CLR_DEF_CHECK_BORDER_COLOR); // Color of control checkbox frame this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,CLR_DEF_CHECK_BORDER_OPACITY); // Opacity of the control checkbox frame color this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_BORDER_MOUSE_DOWN);// Color of control checkbox frame when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,CLR_DEF_CHECK_BORDER_MOUSE_OVER);// Color of control checkbox frame when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,CLR_DEF_CHECK_FLAG_COLOR); // Control checkbox color this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,CLR_DEF_CHECK_FLAG_OPACITY); // Control checkbox color opacity this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,CLR_DEF_CHECK_FLAG_MOUSE_DOWN); // Control checkbox color when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,CLR_DEF_CHECK_FLAG_MOUSE_OVER); // Control checkbox color when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,CLR_DEF_FORE_COLOR); // Default text color for all control objects this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,CLR_DEF_FORE_COLOR_OPACITY); // Opacity of the default text color for all control objects this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN); // Default control text color when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER); // Default control text color when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE,CLR_DEF_FORE_COLOR); // Text color of the control which is on this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN,CLR_DEF_FORE_COLOR_MOUSE_DOWN); // Default control text color when clicking on the control which is on this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER,CLR_DEF_FORE_COLOR_MOUSE_OVER); // Default control text color when hovering the mouse over the control which is on this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_DOWN,this.BackgroundColor()); // Control background color when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER,this.BackgroundColor()); // Control background color when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,CLR_DEF_CONTROL_STD_BACK_COLOR_ON); // Background color of the control which is on this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,CLR_DEF_CONTROL_STD_BACK_DOWN_ON); // Control background color when clicking on the control which is on this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,CLR_DEF_CONTROL_STD_BACK_OVER_ON); // Control background color when clicking on the control which is on this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,CLR_DEF_BORDER_MOUSE_DOWN); // Control frame color when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,CLR_DEF_BORDER_MOUSE_OVER); // Control frame color when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,false); // Toggle flag of the control featuring a button this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,false); // Status of the Toggle control featuring a button } else { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",this.m_name); } } //+------------------------------------------------------------------+
La propiedad ForeColor de la casilla de verificación está ahora escrita con los valores de BorderColor, ya que la casilla de verificación tiene su propio color.
El segundo constructor se implementa de forma idéntica y no requiere de análisis aquí.
En el método que crea la estructura del objeto, escribimos las nuevas propiedades del objeto que se guardarán en los campos de la estructura:
//+------------------------------------------------------------------+ //| Create the object structure | //+------------------------------------------------------------------+ bool CGCnvElement::ObjectToStruct(void) { //--- Save integer properties this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID); // Element ID this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE); // Graphical element type //---... //---... this.m_struct_obj.check_flag_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN);// Control checkbox color when clicking on the control this.m_struct_obj.check_flag_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER);// Control checkbox color when hovering the mouse over the control this.m_struct_obj.fore_color_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN); // Default control text color when clicking on the control this.m_struct_obj.fore_color_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER); // Default control text color when hovering the mouse over the control this.m_struct_obj.fore_color_toggle=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE); // Text color of the control which is on this.m_struct_obj.fore_color_toggle_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN);// Default control text color when clicking on the control which is on this.m_struct_obj.fore_color_toggle_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER);// Default control text color when hovering the mouse over the control which is on this.m_struct_obj.background_color_toggle=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE); // Background color of the control which is on this.m_struct_obj.background_color_toggle_mouse_down=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN);// Control background color when clicking on the control which is on this.m_struct_obj.background_color_toggle_mouse_over=(color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER);// Control background color when hovering the mouse over the control which is on this.m_struct_obj.button_toggle=(bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE); // Toggle flag of the control featuring a button this.m_struct_obj.button_state=(bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_STATE); // Status of the Toggle control featuring a button //--- Save real properties //--- Save string properties ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj); // Graphical element object name ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res); // Graphical resource name ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TEXT),this.m_struct_obj.text); // Graphical element text //--- Save the structure to the uchar array ::ResetLastError(); if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY,true); return false; } return true; } //+------------------------------------------------------------------+
Y en el método que crea el objeto de su estructura, escribimos la configuración de las nuevas propiedades del objeto desde los campos de la estructura:
//+------------------------------------------------------------------+ //| Create the object from the structure | //+------------------------------------------------------------------+ void CGCnvElement::StructToObject(void) { //--- Save integer properties this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id); // Element ID this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type); // Graphical element type //---... //---... this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,this.m_struct_obj.check_flag_color_mouse_over); // Control checkbox color when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,this.m_struct_obj.fore_color_mouse_down); // Default control text color when clicking on the control this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,this.m_struct_obj.fore_color_mouse_over); // Default control text color when hovering the mouse over the control this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE,this.m_struct_obj.fore_color_toggle); // Text color of the control which is on this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN,this.m_struct_obj.fore_color_toggle_mouse_down);// Default control text color when clicking on the control which is on this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER,this.m_struct_obj.fore_color_toggle_mouse_over);// Default control text color when hovering the mouse over the control which is on this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,this.m_struct_obj.background_color_toggle); // Background color of the control which is on this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,this.m_struct_obj.background_color_toggle_mouse_down);// Control background color when clicking on the control which is on this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,this.m_struct_obj.background_color_toggle_mouse_over);// Control background color when clicking on the control which is on this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,this.m_struct_obj.button_toggle); // Toggle flag of the control featuring a button this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,this.m_struct_obj.button_state); // Status of the Toggle control featuring a button //--- Save real properties //--- Save string properties this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj)); // Graphical element object name this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res)); // Graphical resource name this.SetProperty(CANV_ELEMENT_PROP_TEXT,::CharArrayToString(this.m_struct_obj.text)); // Graphical element text } //+------------------------------------------------------------------+
En el constructor de la clase del objeto sombra, en el archivo \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh, transmitimos la bandera que indica la necesidad de guardar el color inicial al establecer el color de fondo:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CShadowObj::CShadowObj(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_SHADOW_OBJ,chart_id,subwindow,name,x,y,w,h) { this.m_type=OBJECT_DE_TYPE_GSHADOW; CGCnvElement::SetBackgroundColor(clrNONE,true); CGCnvElement::SetOpacity(0); CGCnvElement::SetActive(false); this.m_opacity=CLR_DEF_SHADOW_OPACITY; this.m_blur=DEF_SHADOW_BLUR; color gray=CGCnvElement::ChangeColorSaturation(this.ChartBackgroundColor(),-100); this.m_color=CGCnvElement::ChangeColorLightness(gray,-50); this.m_shadow=false; this.m_visible=true; CGCnvElement::Erase(); } //+------------------------------------------------------------------+
Aquí no necesitamos almacenar el color totalmente transparente, pero dejaremos que se guarde para evitar valores de propiedad no inicializados.
El objeto de formulario es el objeto básico del elemento gráfico en el que se escribe la funcionalidad de interacción con el ratón. También escribiremos un manejador de eventos virtual para el ratón y manejadores virtuales para cada evento determinado por el manejador de eventos.
El manejador de eventos será virtual, por si tenemos que redefinirlo en las clases heredadas, pero los manejadores de cada uno de los eventos del ratón ya son conocidos, y se entiende que son virtuales, ya que para cada uno de los objetos herederos de la clase de objeto de formulario, la gestión de uno u otro evento de ratón será individual. Para restaurar el estado de un objeto y sus colores después de que el cursor se haya alejado de su área activa, necesitaremos un método que gestione este estado (alejando el cursor del objeto). Como al alejarse el cursor de un objeto, automáticamente se activa otro objeto que estaba bajo el cursor, o ningún objeto, necesitaremos hacer un ciclo a través de todos los objetos WinForms para procesar dicho estado, determinar su último estado y procesarlo en caso de que su último estado haya sido el del ratón. y luego cambiar ese estado a uno que corresponda a su estado actual. Así, solo procesaremos el cursor fuera del objeto una vez y pondremos el objeto en su estado correcto: el cursor está fuera del formulario.
En el archivo del objeto formulario \MQL5\Include\DoEasy\Objects\Graph\Form.mqh, en la sección protegida de la clase, declararemos dos variables: una para almacenar el último evento del ratón (necesario para el manejador del último evento) y otra para almacenar el color inicial del marco del formulario (ya que puede cambiar su color al interactuar con el ratón, por lo que deberá ser restaurado). Aquí también declaramos los manejadores virtuales para cada uno de los eventos del ratón:
protected: CArrayObj m_list_elements; // List of attached elements CArrayObj m_list_interact; // List of interaction elements CAnimations *m_animations; // Pointer to the animation object CShadowObj *m_shadow_obj; // Pointer to the shadow object CMouseState m_mouse; // "Mouse status" class object ENUM_MOUSE_FORM_STATE m_mouse_form_state; // Mouse status relative to the form ENUM_MOUSE_EVENT m_mouse_event_last; // Last mouse event ushort m_mouse_state_flags; // Mouse status flags int m_offset_x; // Offset of the X coordinate relative to the cursor int m_offset_y; // Offset of the Y coordinate relative to the cursor CArrayObj m_list_tmp; // List for storing the pointers int m_init_x; // Newly created form X coordinate int m_init_y; // Newly created form Y coordinate int m_init_w; // Newly created form width int m_init_h; // Newly created form height color m_border_color_init; // Initial color of the control frame //--- Initialize the variables virtual void Initialize(void); void Deinitialize(void); //--- Create a shadow object void CreateShadowObj(const color colour,const uchar opacity); //--- Return the name of the dependent object string CreateNameDependentObject(const string base_name) const { return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+1)+"_"+base_name; } //--- Update coordinates of bound objects virtual bool MoveDependentObj(const int x,const int y,const bool redraw=false); //--- Create a new bound element and add it to the list of bound objects virtual CGCnvElement *CreateAndAddNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity); //--- Create the list of all interaction objects void CreateListDepInteractObj(CArrayObj *list); //--- Return the flag indicating the presence of the pointer to an object in the list of interaction objects by name bool IsPresentInteractObj(const string name); //--- 'The cursor is outside the form, no mouse buttons are clicked' event handler virtual void MouseOutsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is outside the form, a mouse button is clicked (any)' event handler virtual void MouseOutsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is outside the form, the mouse wheel is being scrolled' event handler virtual void MouseOutsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the form, no mouse buttons are clicked' event handler virtual void MouseInsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the form, a mouse button is clicked (any)' event handler virtual void MouseInsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the form, the mouse wheel is being scrolled' event handler virtual void MouseInsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler virtual void MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, a mouse button is clicked (any)' event handler virtual void MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler virtual void MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, the left mouse button is clicked' event handler virtual void MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the window scrolling area, no mouse buttons are clicked' event handler virtual void MouseScrollAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the window scrolling area, a mouse button is clicked (any)' event handler virtual void MouseScrollAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the window scrolling area, the mouse wheel is being scrolled' event handler virtual void MouseScrollAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam); public:
En la sección pública de la clase, declaramos el método virtual para redibujar el objeto, escribimos el método que retorna la variable que almacena el estado del ratón y
el método que retorna el último evento del ratón.
Los métodos que establecen las dimensiones del marco de cada uno de los lados los haremos virtuales, y después declararemos los eventos de ratón y su último evento:
public: //--- Redraw the object virtual void Redraw(bool redraw) { return; } //--- Create the list of all interaction objects int CreateListInteractObj(void); //--- Return the pointer to the form object in the list of interaction objects CForm *GetInteractForm(const int index) { return this.m_list_interact.At(index); } //--- Return the initial (1) X and (2) Y coordinate, (3) form width and (4) height int GetCoordXInit(void) const { return this.m_init_x; } int GetCoordYInit(void) const { return this.m_init_y; } int GetWidthInit(void) const { return this.m_init_w; } int GetHeightInit(void) const { return this.m_init_h; } //--- Set the initial (1) X and (2) Y coordinate, (3) form width and (4) height void SetCoordXInit(const int value) { this.m_init_x=value; } void SetCoordYInit(const int value) { this.m_init_y=value; } void SetWidthInit(const int value) { this.m_init_w=value; } void SetHeightInit(const int value) { this.m_init_h=value; } //--- (1) Get and (2) return the mouse status relative to the form, as well as cursor (3) X, (4) Y coordinates and (4) the last mouse event ENUM_MOUSE_FORM_STATE MouseFormState(const int id,const long lparam,const double dparam,const string sparam); ENUM_MOUSE_FORM_STATE GetMouseState(void) const { return this.m_mouse_form_state; } int MouseCursorX(void) const { return this.m_mouse.CoordX(); } int MouseCursorY(void) const { return this.m_mouse.CoordY(); } ENUM_MOUSE_EVENT MouseEventLast(void) const { return this.m_mouse_event_last; } //--- Set the flags of mouse scrolling, context menu and the crosshairs tool for the chart void SetChartTools(const bool flag); //--- (1) Set and (2) return the shift of X and Y coordinates relative to the cursor void SetOffsetX(const int value) { this.m_offset_x=value; } void SetOffsetY(const int value) { this.m_offset_y=value; } int OffsetX(void) const { return this.m_offset_x; } int OffsetY(void) const { return this.m_offset_y; } //--- Return the frame size (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides int BorderSizeLeft(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT); } int BorderSizeTop(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP); } int BorderSizeRight(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT); } int BorderSizeBottom(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM);} //--- Set the frame size (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides virtual void SetBorderSizeLeft(const uint value) { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_LEFT,value); } virtual void SetBorderSizeTop(const uint value) { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_TOP,value); } virtual void SetBorderSizeRight(const uint value) { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_RIGHT,value); } virtual void SetBorderSizeBottom(const uint value) { this.SetProperty(CANV_ELEMENT_PROP_BORDER_SIZE_BOTTOM,value); } //--- Update the coordinates (shift the canvas) virtual bool Move(const int x,const int y,const bool redraw=false); //--- Set the priority of a graphical object for receiving the event of clicking on a chart virtual bool SetZorder(const long value,const bool only_prop); //--- Set the object above all virtual void BringToTop(void); //--- Event handler virtual void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Mouse event handler virtual void OnMouseEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Last mouse event handler virtual void OnMouseEventPostProcessing(void); //--- Constructors
Vamos a modificar el método que establece el color del marco del objeto para poder determinar a partir del estado de la bandera si debemos mantener el color original del marco; también escribiremos los métodos para establecer y retornar el color original del marco del objeto:
//+------------------------------------------------------------------+ //| Methods of simplified access to object properties | //+------------------------------------------------------------------+ //--- (1) Set and (2) return the control frame color void SetBorderColor(const color colour,const bool set_init_color) { this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR,colour); if(set_init_color) this.SetBorderColorInit(colour); } color BorderColor(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR); } //--- (1) Set and (2) return the control frame color when clicking the control void SetBorderColorMouseDown(const color colour) { this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN,colour); } color BorderColorMouseDown(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_DOWN); } //--- (1) Set and (2) return the control frame color when hovering the mouse over the control void SetBorderColorMouseOver(const color colour) { this.SetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER,colour); } color BorderColorMouseOver(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BORDER_COLOR_MOUSE_OVER); } //--- (1) Set and (2) get the initial color of the control frame void SetBorderColorInit(const color colour) { this.m_border_color_init=colour; } color BorderColorInit(void) const { return (color)this.m_border_color_init; } //--- (1) Set and (2) return the form shadow color
En el método de inicialización del objeto, guardamos el color original del marco especificando la bandera necesaria en el método para establecer su color, y establecemos los colores por defecto para el color del marco cuando el cursor del ratón se encuentre sobre el objeto y haga clic en él. Inicializamos el último evento del ratón:
//+------------------------------------------------------------------+ //| Initialize the variables | //+------------------------------------------------------------------+ void CForm::Initialize(void) { this.m_list_elements.Clear(); this.m_list_elements.Sort(); this.m_list_interact.Clear(); this.m_list_interact.Sort(); this.m_list_tmp.Clear(); this.m_list_tmp.Sort(); this.m_shadow_obj=NULL; this.m_shadow=false; this.SetBorderSizeTop(DEF_FRAME_WIDTH_SIZE); this.SetBorderSizeBottom(DEF_FRAME_WIDTH_SIZE); this.SetBorderSizeLeft(DEF_FRAME_WIDTH_SIZE); this.SetBorderSizeRight(DEF_FRAME_WIDTH_SIZE); this.SetBorderColor(CLR_DEF_BORDER_COLOR,true); this.SetBorderColorMouseDown(CLR_DEF_BORDER_MOUSE_DOWN); this.SetBorderColorMouseOver(CLR_DEF_BORDER_MOUSE_OVER); this.m_gradient_v=true; this.m_gradient_c=false; this.m_mouse_state_flags=0; this.m_mouse_event_last=MOUSE_EVENT_NO_EVENT; this.m_offset_x=0; this.m_offset_y=0; this.m_init_x=0; this.m_init_y=0; this.m_init_w=0; this.m_init_h=0; CGCnvElement::SetInteraction(false); this.m_animations=new CAnimations(CGCnvElement::GetObject()); this.m_list_tmp.Add(this.m_animations); } //+------------------------------------------------------------------+
En todos los métodos de creación de objetos en los que se requiere un color de fondo, escribimos una bandera. En el método CreateAndAddNewElement():
obj.SetBackgroundColor(colour,true);
En el método que establece el esquema de colores:
//+------------------------------------------------------------------+ //| Set the color scheme | //+------------------------------------------------------------------+ void CForm::SetColorTheme(const ENUM_COLOR_THEMES theme,const uchar opacity) { if(this.m_shadow && this.m_shadow_obj!=NULL) this.SetColorShadow(array_color_themes[theme][COLOR_THEME_COLOR_FORM_SHADOW]); this.SetOpacity(opacity); this.SetBackgroundColor(array_color_themes[theme][COLOR_THEME_COLOR_FORM_BG],true); this.SetBorderColor(array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME],true); } //+------------------------------------------------------------------+
En cada caso, ahora se mantendrá el color de fondo original del elemento.
En el método que establece y retorna el estado del ratón respecto al formulario, antes declarábamos la variable local en la que escribíamos el estado del ratón y retornábamos el valor de esa variable. Ahora tenemos una variable de este tipo en la clase para almacenar este valor.
Aquí es donde escribiremos los valores y los retornaremos desde el método:
//+------------------------------------------------------------------+ //| Set and get the mouse status relative to the form | //+------------------------------------------------------------------+ ENUM_MOUSE_FORM_STATE CForm::MouseFormState(const int id,const long lparam,const double dparam,const string sparam) { //--- Get the mouse status relative to the form, as well as the states of mouse buttons and Shift/Ctrl keys this.m_mouse_form_state=MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED; ENUM_MOUSE_BUTT_KEY_STATE state=this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam); //--- Get the mouse status flags from the CMouseState class object and save them in the variable this.m_mouse_state_flags=this.m_mouse.GetMouseFlags(); //--- If the cursor is inside the form if(CGCnvElement::CursorInsideElement(m_mouse.CoordX(),m_mouse.CoordY())) { //--- Set bit 8 responsible for the "cursor inside the form" flag this.m_mouse_state_flags |= (0x0001<<8); //--- If the cursor is inside the active area, set bit 9 "cursor inside the active area" if(CGCnvElement::CursorInsideActiveArea(m_mouse.CoordX(),m_mouse.CoordY())) this.m_mouse_state_flags |= (0x0001<<9); //--- otherwise, release the bit "cursor inside the active area" else this.m_mouse_state_flags &=0xFDFF; //--- If one of the mouse buttons is clicked, check the cursor location in the active area and //--- return the appropriate value of the pressed key (in the active area or the form area) if((this.m_mouse_state_flags & 0x0001)!=0 || (this.m_mouse_state_flags & 0x0002)!=0 || (this.m_mouse_state_flags & 0x0010)!=0) this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : MOUSE_FORM_STATE_INSIDE_FORM_PRESSED); //--- otherwise, if not a single mouse button is pressed else { //--- if the mouse wheel is scrolled, return the appropriate wheel scrolling value (in the active area or the form area) if((this.m_mouse_state_flags & 0x0080)!=0) this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : MOUSE_FORM_STATE_INSIDE_FORM_WHEEL); //--- otherwise, return the appropriate value of the unpressed key (in the active area or the form area) else this.m_mouse_form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED); } } //--- If the cursor is outside the form else { //--- return the appropriate button value in an inactive area this.m_mouse_form_state= ( ((this.m_mouse_state_flags & 0x0001)!=0 || (this.m_mouse_state_flags & 0x0002)!=0 || (this.m_mouse_state_flags & 0x0010)!=0) ? MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED : MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED ); } return this.m_mouse_form_state; } //+------------------------------------------------------------------+
Manejador de eventos del ratón:
//+------------------------------------------------------------------+ //| Mouse event handler | //+------------------------------------------------------------------+ void CForm::OnMouseEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { switch(id) { //--- The cursor is outside the form, the mouse buttons are not clicked //--- The cursor is outside the form, any mouse button is clicked //--- The cursor is outside the form, the mouse wheel is being scrolled case MOUSE_EVENT_OUTSIDE_FORM_NOT_PRESSED : case MOUSE_EVENT_OUTSIDE_FORM_PRESSED : case MOUSE_EVENT_OUTSIDE_FORM_WHEEL : break; //--- The cursor is inside the form, the mouse buttons are not clicked case MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED : this.MouseInsideNotPressedHandler(id,lparam,dparam,sparam); break; //--- The cursor is inside the form, any mouse button is clicked case MOUSE_EVENT_INSIDE_FORM_PRESSED : this.MouseInsidePressedHandler(id,lparam,dparam,sparam); break; //--- The cursor is inside the form, the mouse wheel is being scrolled case MOUSE_EVENT_INSIDE_FORM_WHEEL : this.MouseInsideWhellHandler(id,lparam,dparam,sparam); break; //--- The cursor is inside the active area, the mouse buttons are not clicked case MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED : this.MouseActiveAreaNotPressedHandler(id,lparam,dparam,sparam); break; //--- The cursor is inside the active area, any mouse button is clicked case MOUSE_EVENT_INSIDE_ACTIVE_AREA_PRESSED : this.MouseActiveAreaPressedHandler(id,lparam,dparam,sparam); break; //--- The cursor is inside the active area, the mouse wheel is being scrolled case MOUSE_EVENT_INSIDE_ACTIVE_AREA_WHEEL : this.MouseActiveAreaWhellHandler(id,lparam,dparam,sparam); break; //--- The cursor is inside the active area, left mouse button is released case MOUSE_EVENT_INSIDE_ACTIVE_AREA_RELEASED : this.MouseActiveAreaReleasedHandler(id,lparam,dparam,sparam); break; //--- The cursor is within the window scrolling area, the mouse buttons are not clicked case MOUSE_EVENT_INSIDE_SCROLL_AREA_NOT_PRESSED : this.MouseScrollAreaNotPressedHandler(id,lparam,dparam,sparam); break; //--- The cursor is within the window scrolling area, any mouse button is clicked case MOUSE_EVENT_INSIDE_SCROLL_AREA_PRESSED : this.MouseScrollAreaPressedHandler(id,lparam,dparam,sparam); break; //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled case MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL : this.MouseScrollAreaWhellHandler(id,lparam,dparam,sparam); break; //--- MOUSE_EVENT_NO_EVENT default: break; } this.m_mouse_event_last=(ENUM_MOUSE_EVENT)id; } //+------------------------------------------------------------------+
Aquí, según el identificador del evento del ratón, llamamos para cada evento a su correspondiente método de manejo. Finalmente, guardamos el evento transmitido al método en una variable que almacena el último evento del ratón. De esta forma, primero procesaremos el evento y luego lo registraremos como el último evento procesado. Todos los métodos llamados aquí, analizados a continuación, son virtuales y deberán redefinirse en las clases herederas.
Manejadores virtuales para cada uno de los eventos del ratón:
//+------------------------------------------------------------------+ //| 'The cursor is outside the form, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CForm::MouseOutsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is outside the form, | //| a mouse button is clicked (any) | //+------------------------------------------------------------------+ void CForm::MouseOutsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is outside the form, | //| the mouse wheel is being scrolled | //+------------------------------------------------------------------+ void CForm::MouseOutsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is inside the form, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CForm::MouseInsideNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is inside the form, | //| a mouse button is clicked (any) | //+------------------------------------------------------------------+ void CForm::MouseInsidePressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is inside the form, | //| the mouse wheel is being scrolled | //+------------------------------------------------------------------+ void CForm::MouseInsideWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CForm::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| a mouse button is clicked (any) | //+------------------------------------------------------------------+ void CForm::MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| the mouse wheel is being scrolled | //+------------------------------------------------------------------+ void CForm::MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| left mouse button released | //+------------------------------------------------------------------+ void CForm::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is inside the window scrolling area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CForm::MouseScrollAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is inside the window scrolling area, | //| a mouse button is clicked (any) | //+------------------------------------------------------------------+ void CForm::MouseScrollAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+ //| 'The cursor is inside the window scrolling area, | //| the mouse wheel is being scrolled | //+------------------------------------------------------------------+ void CForm::MouseScrollAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { return; } //+------------------------------------------------------------------+
Los métodos no hacen nada, ya que el procesamiento completo de cada evento es individual para cada uno de los objetos herederos del objeto de formulario, y deberá redefinirse en cada objeto heredero de ser necesario.
Manejador virtual del último evento del ratón:
//+------------------------------------------------------------------+ //| Last mouse event handler | //+------------------------------------------------------------------+ void CForm::OnMouseEventPostProcessing(void) { ENUM_MOUSE_FORM_STATE state=GetMouseState(); switch(state) { //--- The cursor is outside the form, the mouse buttons are not clicked //--- The cursor is outside the form, any mouse button is clicked //--- The cursor is outside the form, the mouse wheel is being scrolled case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL : if(MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED) { this.SetBackgroundColor(this.BackgroundColorInit(),false); this.SetBorderColor(this.BorderColorInit(),false); this.Redraw(true); this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT); } break; //--- The cursor is inside the form, the mouse buttons are not clicked case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED : break; //--- The cursor is inside the form, any mouse button is clicked case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED : break; //--- The cursor is inside the form, the mouse wheel is being scrolled case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL : break; //--- The cursor is inside the active area, the mouse buttons are not clicked case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : break; //--- The cursor is inside the active area, any mouse button is clicked case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : break; //--- The cursor is inside the active area, the mouse wheel is being scrolled case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : break; //--- The cursor is inside the active area, left mouse button is released case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED : break; //--- The cursor is within the window scrolling area, the mouse buttons are not clicked case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED : break; //--- The cursor is within the window scrolling area, any mouse button is clicked case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED : break; //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL : break; //--- MOUSE_EVENT_NO_EVENT default: break; } } //+------------------------------------------------------------------+
El método también es virtual, y deberá redefinirse en las clases herederas de ser necesario.
De qué se encarga: si el estado del ratón para el objeto está definido como "cursor fuera del formulario" (no importa el estado de los botones y la ruleta), entonces comprobaremos cuál ha sido el último evento de ratón para el objeto. Si es "cursor dentro del área activa y sin botón del ratón pulsado", entonces considera que el cursor se ha retirado del objeto y procesa este evento - establece el color de fondo original, el color del marco del objeto original, redibuja el objeto y escribe su evento actual en la variable que almacena el último evento de ratón.
Teniendo en cuenta que todos los valores constantes de la enumeración ENUM_MOUSE_EVENT difieren de las constantes de la enumeración ENUM_MOUSE_FORM_STATE (con la misma composición y secuencia) en el valor de la constante MOUSE_EVENT_NO_EVENT de la enumeración ENUM_MOUSE_EVENT, para obtener el valor correcto, deberemos sumar el valor de la constante MOUSE_EVENT_NO_EVENT al valor del evento de ratón obtenido en la variable de estado ENUM_MOUSE_FORM_STATE.
El texto que se encuentre en el formulario, como el texto del botón, también puede (pero no necesariamente siempre y en todos los objetos, solo a voluntad) cambiar de color cuando se pasa el cursor por encima o se hace clic en el objeto. Nuestros métodos para el procesamiento de texto de los objetos WinForms se encuentran en la clase básica de los objetos WinForms, en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh.
En la sección protegida de la clase, declaramos una variable para almacenar el color original del texto del objeto. En la sección pública, declaramos los métodos que retornan la lista de objetos adjuntos según el tipo, el número de elementos adjuntos según el tipo y el método que retorna el puntero a un objeto adjunto según el tipo.
Asimismo, escribimos los métodos para establecer y retornar los colores del texto de los objetos adicionales:
//+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+ class CWinFormBase : public CForm { protected: color m_fore_color_init; // Initial color of the control text private: //--- Return the font flags uint GetFontFlags(void); public: //--- Return by type the (1) list, (2) the number of bound controls, (3) the bound control by index in the list CArrayObj *GetListElementsByType(const ENUM_GRAPH_ELEMENT_TYPE type); int ElementsTotalByType(const ENUM_GRAPH_ELEMENT_TYPE type); CGCnvElement *GetElementByType(const ENUM_GRAPH_ELEMENT_TYPE type,const int index); //--- Clear the element filling it with color and opacity virtual void Erase(const color colour,const uchar opacity,const bool redraw=false); //--- Clear the element with a gradient fill virtual void Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false); //--- Clear the element completely virtual void Erase(const bool redraw=false); //--- Redraw the object virtual void Redraw(bool redraw); //--- Set the new size for the (1) current object and (2) the object specified by index virtual bool Resize(const int w,const int h,const bool redraw); virtual bool Resize(const int index,const int w,const int h,const bool redraw); //--- Constructors CWinFormBase(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); CWinFormBase(const string name) : CForm(::ChartID(),0,name,0,0,0,0) { this.m_type=OBJECT_DE_TYPE_GWF_BASE; } //--- (1) Set and (2) return the default text color of all panel objects void SetForeColor(const color clr,const bool set_init_color) { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR,clr); if(set_init_color) this.SetForeColorInit(clr); } color ForeColor(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR); } //--- (1) Set and (2) return the initial default text color of all panel objects void SetForeColorInit(const color clr) { this.m_fore_color_init=clr; } color ForeColorInit(void) const { return (color)this.m_fore_color_init; } //--- (1) Set and (2) return the default text color opacity of all panel objects void SetForeColorOpacity(const uchar value) { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY,value); } uchar ForeColorOpacity(void) const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_OPACITY); } //--- (1) Set and (2) return the control text color when clicking the control void SetForeColorMouseDown(const color clr) { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN,clr); } color ForeColorMouseDown(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN); } //--- (1) Set and (2) return the control text color when hovering the mouse over the control void SetForeColorMouseOver(const color clr) { this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER,clr); } color ForeColorMouseOver(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER); } //--- (1) Set and (2) return the element text virtual void SetText(const string text) { this.SetProperty(CANV_ELEMENT_PROP_TEXT,text); } string Text(void) const { return this.GetProperty(CANV_ELEMENT_PROP_TEXT); }
En el constructor de la clase, al establecer el color del texto , indicamos la necesidad de mantener el color original del texto:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CWinFormBase::CWinFormBase(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CForm(chart_id,subwindow,name,x,y,w,h) { //--- Set the graphical element and library object types as a base WinForms object CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BASE); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BASE); this.m_type=OBJECT_DE_TYPE_GWF_BASE; //--- Initialize all variables this.SetText(""); this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY); this.SetFontBoldType(FW_TYPE_NORMAL); this.SetMarginAll(0); this.SetPaddingAll(0); this.SetBorderSizeAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetAutoSize(false,false); CForm::SetCoordXInit(x); CForm::SetCoordYInit(y); CForm::SetWidthInit(w); CForm::SetHeightInit(h); this.m_shadow=false; this.m_gradient_v=true; this.m_gradient_c=false; } //+------------------------------------------------------------------+
En el método que retorna la descripción de una propiedad entera de un elemento , escribimos los bloques de código necesarios para retornar las descripciones de las nuevas propiedades del objeto WInForms:
property==CANV_ELEMENT_PROP_FORE_COLOR_OPACITY ? CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_OPACITY)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN ? CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_DOWN)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER ? CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_MOUSE_OVER)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE ? CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN ? CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_DOWN)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER ? CMessage::Text(MSG_CANV_ELEMENT_PROP_FORE_COLOR_TOGGLE_MOUSE_OVER)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_BACKGROUND_COLOR ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString((color)this.GetProperty(property),true) ) :
...
property==CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_MOUSE_OVER)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_BOLD_TYPE ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BOLD_TYPE)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+FontBoldTypeDescription() ) :
...
property==CANV_ELEMENT_PROP_AUTOCHECK ? CMessage::Text(MSG_CANV_ELEMENT_PROP_AUTOCHECK)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)(bool)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_BUTTON_TOGGLE ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BUTTON_TOGGLE)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)(bool)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_BUTTON_STATE ? CMessage::Text(MSG_CANV_ELEMENT_PROP_BUTTON_STATE)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)(bool)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : property==CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER ? CMessage::Text(MSG_CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER)+ (only_prop ? "" : !this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((color)this.GetProperty(property),true) ) : "" ); } //+------------------------------------------------------------------+ //| Return the description of the control real property | //+------------------------------------------------------------------+
Método que retorna la lista de elementos vinculados según el tipo:
//+------------------------------------------------------------------+ // Return the list of bound controls by type | //+------------------------------------------------------------------+ CArrayObj *CWinFormBase::GetListElementsByType(const ENUM_GRAPH_ELEMENT_TYPE type) { return CSelect::ByGraphCanvElementProperty(this.GetListElements(),CANV_ELEMENT_PROP_TYPE,type,EQUAL); } //+------------------------------------------------------------------+
Aquí solo hay que retornar la lista de elementos gráficos seleccionados de la lista de objetos vinculados según el tipo indicado.
Método que retorna el elemento vinculado por tipo según el índice en la lista:
//+------------------------------------------------------------------+ //| Get (by type) the bound element by index in the list | //+------------------------------------------------------------------+ CGCnvElement *CWinFormBase::GetElementByType(const ENUM_GRAPH_ELEMENT_TYPE type,const int index) { CArrayObj *list=this.GetListElementsByType(type); return list.At(index); } //+------------------------------------------------------------------+
Aquí: recuperamos la lista de objetos según el tipo especificado usando el método descrito anteriormente, y retornamos el puntero al objeto en la lista obtenida según el índice indicado. Si no logramos obtener la lista, o si el índice excede el tamaño de la misma, el método retornará NULL.
Método que retorna por tipo el número de elementos vinculados:
//+------------------------------------------------------------------+ //| Get the list of bound elements by type | //+------------------------------------------------------------------+ int CWinFormBase::ElementsTotalByType(const ENUM_GRAPH_ELEMENT_TYPE type) { CArrayObj *list=this.GetListElementsByType(type); return(list!=NULL ? list.Total() : 0); } //+------------------------------------------------------------------+
Obtenemos una lista de objetos vinculados del tipo especificado y retornamos el número de punteros a los objetos de la lista. Si no hemos logrado obtener la lista, el método retornará cero.
En la clase de objeto de contenedor WinForms básico, en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh, cambiamos el nombre de los métodos para establecer la anchura del marco. Antes usábamos "Frame" en el nombre, pero ahora usaremos "Border":
//--- Set the width of the form frame (1) to the left, (2) at the top, (3) to the right, (4) at the bottom and (5) on all sides of the control virtual void SetBorderSizeLeft(const uint value) { CForm::SetBorderSizeLeft(value); if(this.PaddingLeft()<this.BorderSizeLeft()) this.SetPaddingLeft(this.BorderSizeLeft()); } virtual void SetBorderSizeTop(const uint value) { CForm::SetBorderSizeTop(value); if(this.PaddingTop()<this.BorderSizeTop()) this.SetPaddingTop(this.BorderSizeTop()); } virtual void SetBorderSizeRight(const uint value) { CForm::SetBorderSizeRight(value); if(this.PaddingRight()<this.BorderSizeRight()) this.SetPaddingRight(this.BorderSizeRight()); } virtual void SetBorderSizeBottom(const uint value) { CForm::SetBorderSizeBottom(value); if(this.PaddingBottom()<this.BorderSizeBottom()) this.SetPaddingBottom(this.BorderSizeBottom()); }
Qué es lo importante: mientras que antes estos métodos tenían un nombre diferente, y llamar a un método de la clase padre para establecer la anchura del marco no suponía ningún problema, ahora se producirá una llamada recursiva al propio método. Así que aquí indicaremos explícitamente el contexto desde el que se llamará al método necesario de la clase padre que tiene exactamente el mismo nombre.
En los constructores de la clase, al guardar el color del texto, indicaremos la necesidad de conservar el color original de los rótulos del elemento:
//--- Constructors CContainer(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); CContainer(const string name) : CWinFormBase(::ChartID(),0,name,0,0,0,0) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CONTAINER); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CONTAINER); this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER; this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetFontBoldType(FW_TYPE_NORMAL); this.SetMarginAll(3); this.SetPaddingAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetAutoScroll(false,false); this.SetAutoScrollMarginAll(0); this.SetAutoSize(false,false); this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false); this.Initialize(); } //--- Destructor ~CContainer(); }; //+------------------------------------------------------------------+ //| Constructor indicating the chart and subwindow ID | //+------------------------------------------------------------------+ CContainer::CContainer(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CWinFormBase(chart_id,subwindow,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CONTAINER); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CONTAINER); this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER; this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetFontBoldType(FW_TYPE_NORMAL); this.SetMarginAll(3); this.SetPaddingAll(0); this.SetDockMode(CANV_ELEMENT_DOCK_MODE_NONE,false); this.SetBorderStyle(FRAME_STYLE_NONE); this.SetAutoScroll(false,false); this.SetAutoScrollMarginAll(0); this.SetAutoSize(false,false); this.SetAutoSizeMode(CANV_ELEMENT_AUTO_SIZE_MODE_GROW,false); this.Initialize(); this.SetCoordXInit(x); this.SetCoordYInit(y); this.SetWidthInit(w); this.SetHeightInit(h); } //+------------------------------------------------------------------+
En el método que crea un nuevo elemento vinculado, indicaremos en todas partes la necesidad de guardar el color original:
//+------------------------------------------------------------------+ //| Create a new attached element | //+------------------------------------------------------------------+ bool CContainer::CreateNewElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool activity, const bool redraw) { //--- If the object type is less than the base WinForms object if(element_type<GRAPH_ELEMENT_TYPE_WF_BASE) { //--- report the error and return 'false' CMessage::ToLog(DFUN,MSG_PANEL_OBJECT_ERR_OBJ_MUST_BE_WFBASE); return false; } //--- If failed to create a new graphical element, return 'false' CWinFormBase *obj=CForm::CreateAndAddNewElement(element_type,x,y,w,h,colour,opacity,activity); if(obj==NULL) return false; //--- Set the text color of the created object as that of the base panel obj.SetForeColor(this.ForeColor(),true); //--- Depending on the created object type, switch(obj.TypeGraphElement()) { //--- For the Container, Panel and GroupBox WinForms objects case GRAPH_ELEMENT_TYPE_WF_CONTAINER : case GRAPH_ELEMENT_TYPE_WF_PANEL : case GRAPH_ELEMENT_TYPE_WF_GROUPBOX : //--- set the frame color equal to the background color obj.SetBorderColor(obj.BackgroundColor(),true); break; //--- For the Text Label, CheckBox and RadioButton WinForms objects case GRAPH_ELEMENT_TYPE_WF_LABEL : case GRAPH_ELEMENT_TYPE_WF_CHECKBOX : case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON : //--- set the object text color depending on the one passed to the method: //--- either the container text color, or the one passed to the method. //--- The frame color is set equal to the text color obj.SetForeColor(colour==clrNONE ? this.ForeColor() : colour,true); obj.SetBorderColor(obj.ForeColor(),true); break; //--- For the Button WinForms object case GRAPH_ELEMENT_TYPE_WF_BUTTON : //--- set the object text color as a container text color depending on the one passed to the method: //--- set the background color depending on the one passed to the method: //--- either the default standard control background color, or the one passed to the method. //--- The frame color is set equal to the text color obj.SetForeColor(this.ForeColor(),true); obj.SetBackgroundColor(colour==clrNONE ? CLR_DEF_CONTROL_STD_BACK_COLOR : colour,true); obj.SetBorderColor(obj.ForeColor(),true); obj.SetBorderStyle(FRAME_STYLE_SIMPLE); break; default: break; } //--- If the panel has auto resize enabled and features bound objects, call the resize method if(this.AutoSize() && this.ElementsTotal()>0) this.AutoSizeProcess(redraw); //--- Redraw the panel and all added objects, and return 'true' this.Redraw(redraw); return true; } //+------------------------------------------------------------------+
En el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\GroupBox.mqh de la clase WinForms del objeto GroupBox, en su método de inicialización Initialize(), escribiremos la necesidad de guardar los colores originales al establecer los colores por defecto:
//--- Create an animation object and add it to the list for storing such objects this.m_animations=new CAnimations(CGCnvElement::GetObject()); this.m_list_tmp.Add(this.m_animations); //--- Set a transparent background for the object background and the default color for the frame this.SetBackgroundColor(CLR_CANV_NULL,true); this.SetOpacity(0); this.SetBorderColor(CLR_DEF_FRAME_GBOX_COLOR,true); //--- Set the default color and text opacity, as well as the absence of the object frame this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY); this.SetBorderStyle(FRAME_STYLE_SIMPLE); //--- Set the default text parameters
En el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CommonBase.mqh de la clase básica del objeto WinForms estándar, en su método Initialize(), escribiremos la necesidad de guardar los colores iniciales al establecer los colores por defecto:
//--- Create an animation object and add it to the list for storing such objects this.m_animations=new CAnimations(CGCnvElement::GetObject()); this.m_list_tmp.Add(this.m_animations); //--- Set the transparent color for the object background this.SetBackgroundColor(CLR_CANV_NULL,true); this.SetOpacity(0); //--- Set the default color and text opacity, as well as the absence of the object frame this.SetForeColor(CLR_DEF_FORE_COLOR,true); this.SetForeColorOpacity(CLR_DEF_FORE_COLOR_OPACITY); this.SetBorderStyle(FRAME_STYLE_NONE); //--- Set the default text parameters
El objeto CheckBox WinForms contiene una casilla de verificación y un texto. Al mover el cursor del ratón y pulsar en el botón, el color del propio campo, su marco y la casilla de verificación (marca de verificación) cambiarán en el objeto. Por consiguiente, para todos estos componentes, necesitaremos crear variables para almacenar sus colores originales, y también métodos para establecer los colores correspondientes al evento de ratón.
En el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\CheckBox.mqh, en la sección protegida de la clase, declaramos las variables para almacenar los colores iniciales y los métodos virtuales que manejan los eventos de ratón:
//+------------------------------------------------------------------+ //| CheckBox object class of the WForms controls | //+------------------------------------------------------------------+ class CCheckBox : public CLabel { private: //--- Set X and Y checkbox coordinates void SetCheckFlagCoords(int &x,int &y); //--- Set the corrected text coordinates depending on the text alignment and checkbox void SetCorrectTextCoords(void); protected: int m_text_x; // Text X coordinate int m_text_y; // Text Y coordinate int m_check_x; // Checkbox X coordinate int m_check_y; // Checkbox Y coordinate int m_check_w; // Checkbox width int m_check_h; // Checkbox height color m_check_back_color_init; // Initial color of the checkbox background color m_check_border_color_init; // Initial color of the checkbox background frame color m_check_flag_color_init; // Initial color of the checkbox //--- Set the element width and height automatically virtual bool AutoSetWH(void); //--- Displays the checkbox for the specified state virtual void ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state); //--- (1) Set and (2) return the checkbox size on the element void SetCheckWidth(const int width) { this.m_check_w=(width<5 ? 5 : width); } void SetCheckHeight(const int height) { this.m_check_h=(height<5 ? 5 : height); } int CheckWidth(void) const { return this.m_check_w; } int CheckHeight(void) const { return this.m_check_h; } //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler virtual void MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, a mouse button is clicked (any)' event handler virtual void MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, the left mouse button is clicked' event handler virtual void MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); public:
Modificaremos los métodos para establecer el color de fondo de la casilla, el marco del campo y la propia casilla de tal forma que podamos guardar el color original de campo según el valor de la bandera. Asimismo, añadiremos los métodos para establecer y retornar los colores originales, y declararemos un manejador para el último evento de ratón:
//--- (1) Set and (2) return the flag of the checkbox auto change when it is selected void SetAutoCheck(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_AUTOCHECK,flag); } bool AutoCheck(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_AUTOCHECK); } //--- (1) Set and (2) return the control verification checkbox background color void SetCheckBackgroundColor(const color clr,const bool set_init_color) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR,clr); if(set_init_color) this.SetCheckBackgroundColorInit(clr); } color CheckBackgroundColor(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR); } //--- (1) Set and (2) return the control verification checkbox background color opacity void SetCheckBackgroundColorOpacity(const uchar value) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY,value); } uchar CheckBackgroundColorOpacity(void) const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_OPACITY); } //--- (1) Set and (2) return the color of control checkbox background when clicking the control void SetCheckBackgroundColorMouseDown(const color clr) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN,clr); } color CheckBackgroundColorMouseDown(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_DOWN);} //--- (1) Set and (2) return the color of control checkbox background when hovering the mouse over the control void SetCheckBackgroundColorMouseOver(const color clr) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER,clr); } color CheckBackgroundColorMouseOver(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_BACKGROUND_COLOR_MOUSE_OVER);} //--- (1) Set and (2) return the initial color of the control verification checkbox background void SetCheckBackgroundColorInit(const color clr) { this.m_check_back_color_init=clr; } color CheckBackgroundColorInit(void) const { return (color)this.m_check_back_color_init; } //--- (1) Set and (2) return the control checkbox frame color void SetCheckBorderColor(const color clr,const bool set_init_color) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR,clr); if(set_init_color) this.SetCheckBorderColorInit(clr); } color CheckBorderColor(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR); } //--- (1) Set and (2) return the control checkbox frame color opacity void SetCheckBorderColorOpacity(const uchar value) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY,value); } uchar CheckBorderColorOpacity(void) const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_OPACITY); } //--- (1) Set and (2) return the color of control checkbox frame color when clicking on the control void SetCheckBorderColorMouseDown(const color clr) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN,clr); } color CheckBorderColorMouseDown(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_DOWN); } //--- (1) Set and (2) return the color of the control checkbox frame color when hovering the mouse over the control void SetCheckBorderColorMouseOver(const color clr) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER,clr); } color CheckBorderColorMouseOver(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FORE_COLOR_MOUSE_OVER); } //--- (1) Set and (2) return the initial color of the control verification checkbox frame void SetCheckBorderColorInit(const color clr) { this.m_check_border_color_init=clr; } color CheckBorderColorInit(void) const { return (color)this.m_check_border_color_init; } //--- (1) Set and (2) return the control verification checkbox color void SetCheckFlagColor(const color clr,const bool set_init_color) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR,clr); if(set_init_color) this.SetCheckFlagColorInit(clr); } color CheckFlagColor(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR); } //--- (1) Set and (2) return the control verification checkbox color opacity void SetCheckFlagColorOpacity(const uchar value) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY,value); } uchar CheckFlagColorOpacity(void) const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_OPACITY); } //--- (1) Set and (2) return the color of control checkbox when clicking on the control void SetCheckFlagColorMouseDown(const color clr) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN,clr); } color CheckFlagColorMouseDown(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_DOWN); } //--- (1) Set and (2) return the color of the control checkbox when hovering the mouse over the control void SetCheckFlagColorMouseOver(const color clr) { this.SetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER,clr); } color CheckFlagColorMouseOver(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_CHECK_FLAG_COLOR_MOUSE_OVER); } //--- (1) Set and (2) return the initial color of the control verification checkbox void SetCheckFlagColorInit(const color clr) { this.m_check_flag_color_init=clr; } color CheckFlagColorInit(void) const { return (color)this.m_check_flag_color_init; } //--- Last mouse event handler virtual void OnMouseEventPostProcessing(void); //--- Redraw the object virtual void Redraw(bool redraw); //--- Constructor
En el constructor de la clase, estableceremos los colores por defecto para los diferentes eventos y estados de ratón:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CCheckBox::CCheckBox(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CLabel(chart_id,subwindow,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_CHECKBOX); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_CHECKBOX); this.m_type=OBJECT_DE_TYPE_GWF_COMMON; this.SetCoordX(x); this.SetCoordY(y); this.SetCheckWidth(DEF_CHECK_SIZE); this.SetCheckHeight(DEF_CHECK_SIZE); this.SetWidth(w); this.SetHeight(h); this.Initialize(); this.SetForeColorMouseDown(CLR_DEF_FORE_COLOR_MOUSE_DOWN); this.SetForeColorMouseOver(CLR_DEF_FORE_COLOR_MOUSE_OVER); this.SetCheckBackgroundColor(CLR_DEF_CHECK_BACK_COLOR,true); this.SetCheckBackgroundColorMouseDown(CLR_DEF_CHECK_BACK_MOUSE_DOWN); this.SetCheckBackgroundColorMouseOver(CLR_DEF_CHECK_BACK_MOUSE_OVER); this.SetCheckBorderColor(CLR_DEF_CHECK_BORDER_COLOR,true); this.SetCheckBorderColorMouseDown(CLR_DEF_CHECK_BORDER_MOUSE_DOWN); this.SetCheckBorderColorMouseOver(CLR_DEF_CHECK_BORDER_MOUSE_OVER); this.SetCheckFlagColor(CLR_DEF_CHECK_FLAG_COLOR,true); this.SetCheckFlagColorMouseDown(CLR_DEF_CHECK_FLAG_MOUSE_DOWN); this.SetCheckFlagColorMouseOver(CLR_DEF_CHECK_FLAG_MOUSE_OVER); this.SetWidthInit(this.Width()); this.SetHeightInit(this.Height()); this.SetCoordXInit(x); this.SetCoordYInit(y); this.SetTextAlign(ANCHOR_LEFT); this.m_text_x=0; this.m_text_y=0; this.m_check_x=0; this.m_check_y=0; this.Redraw(false); } //+------------------------------------------------------------------+
No todos los manejadores de eventos de ratón necesitan ser redefinidos aquí. Vamos a analizar los métodos redefinidos de la clase padre.
Manejador de eventos Cursor dentro del área activa, sin botones del ratón presionados:
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CCheckBox::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseOver(),false); this.SetCheckBorderColor(this.CheckBorderColorMouseOver(),false); this.SetCheckFlagColor(this.CheckFlagColorMouseOver(),false); this.Redraw(true); } //+------------------------------------------------------------------+
Si el puntero del ratón se encuentra sobre el área activa del objeto, estableceremos la casilla de verificación, su marco y la propia casilla de verificación con los nuevos colores de fondo establecidos para ese estado de ratón y del objeto, y redibujaremos el objeto.
Controlador de eventos Cursor dentro del área activa, botón del ratón presionado (cualquiera):
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| a mouse button is clicked (any) | //+------------------------------------------------------------------+ void CCheckBox::MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseDown(),false); this.SetCheckBorderColor(this.CheckBorderColorMouseDown(),false); this.SetCheckFlagColor(this.CheckFlagColorMouseDown(),false); this.Redraw(false); } //+------------------------------------------------------------------+
Si el puntero del ratón se encuentra sobre el área activa del objeto, estableceremos la casilla de verificación, su marco y la propia casilla de verificación con los nuevos colores de fondo establecidos para ese estado de ratón y del objeto, y redibujaremos el objeto.
Manejador de eventos Cursor dentro del área activa, botón del ratón sin pulsar (izquierdo):
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| left mouse button released | //+------------------------------------------------------------------+ void CCheckBox::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- The mouse button released outside the element means refusal to interact with the element if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge()) { this.SetCheckBackgroundColor(this.BackgroundColorInit(),false); this.SetCheckBorderColor(this.CheckBorderColorInit(),false); this.SetCheckFlagColor(this.CheckFlagColorInit(),false); //--- Send a test entry to the journal Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel")); } //--- The mouse button released within the element means a click on the control else { this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseOver(),false); this.SetCheckBorderColor(this.CheckBorderColorMouseOver(),false); this.SetCheckFlagColor(this.CheckFlagColorInit(),false); this.SetChecked(!this.Checked()); //--- Send a test entry to the journal Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click")); } this.Redraw(false); } //+------------------------------------------------------------------+
Aquí se procesan dos estados:
cuando el botón del ratón está sin pulsar y el cursor está fuera del área activa del objeto, y
cuando el botón del ratón está sin pulsar y el cursor está dentro del área activa del objeto.
Si el cursor está fuera del objeto, significará que el usuario ha pulsado el botón, luego ha alejado el cursor y ha soltado el botón. En esta situación, el cuadro de selección no deberá cambiar su estado: será una negativa a interactuar con el objeto. Por lo tanto, asignaremos al objeto los colores originales para la casilla y su campo.
Si el cursor está dentro del objeto, significará que el usuario ha pulsado el botón del ratón y luego lo ha soltado, lo cual debería hacer que el estado de la casilla de verificación cambie. Así que vamos a cambiar el color del campo, del marco del campo y de la casilla de verificación al color establecido para el objeto cuando el cursor pasa por encima de la zona activa (aunque se haya soltado el botón del ratón, el cursor permanece sobre el objeto, por lo que el color deberá ser el mismo que cuando el cursor está pasando por encima del objeto), y también a establecer el estado contrario para la casilla de verificación.
Al terminar, redibujaremos el objeto. Para cada una de las situaciones analizadas, mostraremos en el diario de registro una entrada de texto sobre el estado de la casilla. Posteriormente, al activarse la casilla, sustituiremos la muestra de la entrada por el envío de un evento para su posterior procesamiento.
Manejador virtual del último evento del ratón:
//+------------------------------------------------------------------+ //| Last mouse event handler | //+------------------------------------------------------------------+ void CCheckBox::OnMouseEventPostProcessing(void) { ENUM_MOUSE_FORM_STATE state=GetMouseState(); switch(state) { //--- The cursor is outside the form, the mouse buttons are not clicked //--- The cursor is outside the form, any mouse button is clicked //--- The cursor is outside the form, the mouse wheel is being scrolled case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL : if(MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED) { this.SetBackgroundColor(this.BackgroundColorInit(),false); this.SetBorderColor(this.BorderColorInit(),false); this.SetCheckBackgroundColor(this.CheckBackgroundColorInit(),false); this.SetCheckBorderColor(this.CheckBorderColorInit(),false); this.SetCheckFlagColor(this.CheckFlagColorInit(),false); this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT); this.Redraw(false); } break; //--- The cursor is inside the form, the mouse buttons are not clicked case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED : break; //--- The cursor is inside the form, any mouse button is clicked case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED : break; //--- The cursor is inside the form, the mouse wheel is being scrolled case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL : break; //--- The cursor is inside the active area, the mouse buttons are not clicked case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : break; //--- The cursor is inside the active area, any mouse button is clicked case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : break; //--- The cursor is inside the active area, the mouse wheel is being scrolled case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : break; //--- The cursor is inside the active area, left mouse button is released case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED : break; //--- The cursor is within the window scrolling area, the mouse buttons are not clicked case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED : break; //--- The cursor is within the window scrolling area, any mouse button is clicked case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED : break; //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL : break; //--- MOUSE_EVENT_NO_EVENT default: break; } } //+------------------------------------------------------------------+
Aquí solo se procesa el estado cuando el ratón está fuera del objeto, y el estado anterior era "Cursor dentro del área activa y sin botones del ratón pulsados". En esta situación, estableceremos todas las propiedades de color modificables del objeto en sus valores originales y asignaremos al último evento de ratón su estado actual.
La clase de objeto WinForms RadioButton en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\RadioButton.mqh se hereda de la clase recién finalizada del objeto CheckBox, por lo que aquí tendremos que hacer menos cambios.
Este objeto siempre está marcado y desmarcado dependiendo del otro objeto similar con el que está emparejado: si está marcado en un objeto, estará desmarcado en el otro, y viceversa. Por lo tanto, todo lo que debemos hacer aquí es redefinir un método virtual de la clase padre:
//+------------------------------------------------------------------+ //| CheckBox object class of the WForms controls | //+------------------------------------------------------------------+ class CRadioButton : public CCheckBox { private: protected: //--- Displays the checkbox for the specified state virtual void ShowControlFlag(const ENUM_CANV_ELEMENT_CHEK_STATE state); //--- 'The cursor is inside the active area, the left mouse button is clicked' event handler virtual void MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); public:
Manejador de eventos Cursor dentro del área activa, botón del ratón sin pulsar (izquierdo):
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| left mouse button released | //+------------------------------------------------------------------+ void CRadioButton::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- The mouse button released outside the element means refusal to interact with the element if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge()) { this.SetCheckBackgroundColor(this.BackgroundColorInit(),false); this.SetCheckBorderColor(this.CheckBorderColorInit(),false); this.SetCheckFlagColor(this.CheckFlagColorInit(),false); Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel")); } //--- The mouse button released within the element means a click on the control else { this.SetCheckBackgroundColor(this.CheckBackgroundColorMouseOver(),false); this.SetCheckBorderColor(this.CheckBorderColorMouseOver(),false); this.SetCheckFlagColor(this.CheckFlagColorInit(),false); Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click")); } this.Redraw(false); } //+------------------------------------------------------------------+
El método es similar al analizado para la clase de objeto CheckBox, salvo que aquí no necesitaremos establecer la casilla de verificación en su valor opuesto en el bloque que procesa la pulsación del botón dentro del área activa del objeto.
El objeto WinForms "Botón" (Button). El botón puede ser de dos tipos:
- Al ser pulsado, el botón se accionará y volverá a su estado original,
- Al ser pulsado, el botón se accionará y permanecerá en la posición pulsada; si se vuelve a pulsar, se invertirá su estado (disparador - Toggle-Button).
En consecuencia, necesitaremos una bandera que determine de qué botón se trata, una bandera para identificar el estado del botón de activación (pulsado/sin pulsar) y, por supuesto, otro conjunto para identificar los colores del botón cuando este sea pulsado (color de fondo, color al colocar el cursor y color de la pulsación del botón).
En el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\Button.mqh, en la sección privada de la clase, declaramos los arrays necesarios para almacenar los colores de los nuevos estados de los botones:
//+------------------------------------------------------------------+ //| Label object class of WForms controls | //+------------------------------------------------------------------+ class CButton : public CLabel { private: int m_text_x; // Text X coordinate int m_text_y; // Text Y coordinate color m_array_colors_bg_tgl[]; // Array of element background colors for the 'enabled' state color m_array_colors_bg_tgl_dwn[]; // Array of control background colors for the 'enabled' state when clicking on the control color m_array_colors_bg_tgl_ovr[]; // Array of control background colors for the 'enabled' state when hovering the mouse over the control color m_array_colors_bg_tgl_init[]; // Array of initial element background colors for the 'enabled' state protected:
En la sección protegida de la clase, declaramos los manejadores de eventos de ratón de la clase padre para que sean redefinidos aquí:
protected: //--- Set the element width and height automatically virtual bool AutoSetWH(void); //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler virtual void MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, a mouse button is clicked (any)' event handler virtual void MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); //--- 'The cursor is inside the active area, the left mouse button is clicked' event handler virtual void MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam); public:
En la sección pública de la clase, escribimos los métodos para establecer y retornar la bandera del botón-disparador, los métodos para establecer y retornar el estado del botón-disparador, los métodos para trabajar con los colores de los nuevos estados del botón y declaramos el método-manejador para el último evento de ratón:
public: //--- Redraw the object virtual void Redraw(bool redraw); //--- (1) Set and (2) return the mode of the element auto resizing depending on the content void SetAutoSizeMode(const ENUM_CANV_ELEMENT_AUTO_SIZE_MODE mode,const bool redraw) { ENUM_CANV_ELEMENT_AUTO_SIZE_MODE prev=this.AutoSizeMode(); if(prev==mode) return; this.SetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE,mode); } ENUM_CANV_ELEMENT_AUTO_SIZE_MODE AutoSizeMode(void) const { return (ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)this.GetProperty(CANV_ELEMENT_PROP_AUTOSIZE_MODE); } //--- (1) Set and (2) return the control Toggle flag void SetToggleFlag(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE,flag); if(this.Toggle()) this.SetColorsToggleON(CLR_DEF_CONTROL_STD_BACK_COLOR_ON,CLR_DEF_CONTROL_STD_BACK_DOWN_ON,CLR_DEF_CONTROL_STD_BACK_OVER_ON,true); } bool Toggle(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_TOGGLE); } //--- (1) Set and (2) return the Toggle control status void SetState(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_BUTTON_STATE,flag); } bool State(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_BUTTON_STATE); } //--- (1,2) Set and (3) return the main background color for the 'enabled' status void SetBackgroundColorToggleON(const color colour,const bool set_init_color) { this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,colour); color arr[1]; arr[0]=colour; this.CopyArraysColors(this.m_array_colors_bg_tgl,arr,DFUN); if(set_init_color) this.CopyArraysColors(this.m_array_colors_bg_tgl_init,arr,DFUN); } void SetBackgroundColorsToggleON(color &colors[],const bool set_init_colors) { this.CopyArraysColors(this.m_array_colors_bg_tgl,colors,DFUN); this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE,this.m_array_colors_bg_tgl[0]); if(set_init_colors) this.CopyArraysColors(this.m_array_colors_bg_tgl_init,colors,DFUN); } color BackgroundColorToggleON(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE); } //--- (1,2) Set and (3) return the background color when clicking on the control for the 'enabled' status void SetBackgroundColorToggleONMouseDown(const color colour) { this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,colour); color arr[1]; arr[0]=colour; this.CopyArraysColors(this.m_array_colors_bg_tgl_dwn,arr,DFUN); } void SetBackgroundColorsToggleONMouseDown(color &colors[]) { this.CopyArraysColors(this.m_array_colors_bg_tgl_dwn,colors,DFUN); this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN,this.m_array_colors_bg_dwn[0]); } color BackgroundColorToggleONMouseDown(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_DOWN); } //--- (1,2) Set and (3) return the background color when hovering the mouse over the control for the 'enabled' status void SetBackgroundColorToggleONMouseOver(const color colour) { this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,colour); color arr[1]; arr[0]=colour; this.CopyArraysColors(this.m_array_colors_bg_tgl_ovr,arr,DFUN); } void SetBackgroundColorsToggleONMouseOver(color &colors[]) { this.CopyArraysColors(this.m_array_colors_bg_tgl_ovr,colors,DFUN); this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER,this.m_array_colors_bg_ovr[0]); } color BackgroundColorToggleONMouseOver(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_TOGGLE_MOUSE_OVER); } //--- Return the initial main background color for the 'enabled' status color BackgroundColorToggleONInit(void) const { return this.m_array_colors_bg_tgl_init[0]; } //--- Set the colors for the 'enabled' status void SetColorsToggleON(const color back,const color back_down,const color back_over,const bool set_init_color); //--- Last mouse event handler virtual void OnMouseEventPostProcessing(void); //--- Constructor CButton(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); }; //+------------------------------------------------------------------+
Los métodos son idénticos a los mismos métodos encargados de establecer y retornar los colores de los objetos en las otras clases que hemos examinado con anterioridad.
En el constructor de la clase, establecemos los colores por defecto para el fondo del objeto, su valor de opacidad y los valores de sus colores de fondo al pasar el ratón por encima del botón y clicar en él. A continuación, establecemos la bandera del botón normal (no del interruptor) y, en consecuencia, el estado del botón del interruptor en false:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CButton::CButton(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CLabel(chart_id,subwindow,name,x,y,w,h) { CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BUTTON); CGCnvElement::SetProperty(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_WF_BUTTON); this.m_type=OBJECT_DE_TYPE_GWF_COMMON; this.SetCoordX(x); this.SetCoordY(y); this.SetWidth(w); this.SetHeight(h); this.Initialize(); this.SetBackgroundColor(CLR_DEF_CONTROL_STD_BACK_COLOR,true); this.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_STD_MOUSE_DOWN); this.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_STD_MOUSE_OVER); this.SetOpacity(CLR_DEF_CONTROL_STD_OPACITY); this.SetTextAlign(ANCHOR_CENTER); this.SetMarginAll(3); this.SetWidthInit(this.Width()); this.SetHeightInit(this.Height()); this.SetCoordXInit(x); this.SetCoordYInit(y); this.SetToggleFlag(false); this.SetState(false); this.Redraw(false); } //+------------------------------------------------------------------+
Después redefinimos los manejadores de eventos del ratón.
Manejador de eventos Cursor dentro del área activa, sin botones del ratón presionados:
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| no mouse buttons are clicked' event handler | //+------------------------------------------------------------------+ void CButton::MouseActiveAreaNotPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- If this is a simple button, set the color for the "The cursor is over the active area, the mouse button is not clicked" status if(!this.Toggle()) this.SetBackgroundColor(this.BackgroundColorMouseOver(),false); //--- If this is the toggle button, set the color for the status depending on whether the button is pressed or not else this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleONMouseOver() : this.BackgroundColorMouseOver(),false); //--- Set the frame color for the status this.SetBorderColor(this.BorderColorMouseOver(),false); //--- Redraw the object this.Redraw(false); } //+------------------------------------------------------------------+
Controlador de eventos Cursor dentro del área activa, botón del ratón presionado (cualquiera):
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| a mouse button is clicked (any) | //+------------------------------------------------------------------+ void CButton::MouseActiveAreaPressedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- If this is a simple button, set the color for the "The cursor is over the active area, the mouse button is clicked" status if(!this.Toggle()) this.SetBackgroundColor(this.BackgroundColorMouseDown(),false); //--- If this is the toggle button, set the color for the status depending on whether the button is pressed or not else this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleONMouseDown() : this.BackgroundColorMouseDown(),false); //--- Set the frame color for the status this.SetBorderColor(this.BorderColorMouseDown(),false); //--- Redraw the object this.Redraw(false); } //+------------------------------------------------------------------+
Manejador de eventos Cursor dentro del área activa, botón del ratón sin pulsar (izquierdo):
//+------------------------------------------------------------------+ //| 'The cursor is inside the active area, | //| left mouse button released | //+------------------------------------------------------------------+ void CButton::MouseActiveAreaReleasedHandler(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- The mouse button released outside the element means refusal to interact with the element if(lparam<this.CoordX() || lparam>this.RightEdge() || dparam<this.CoordY() || dparam>this.BottomEdge()) { //--- If this is a simple button, set the initial background color if(!this.Toggle()) this.SetBackgroundColor(this.BackgroundColorInit(),false); //--- If this is the toggle button, set the initial color depending on whether the button is pressed or not else this.SetBackgroundColor(!this.State() ? this.BackgroundColorInit() : this.BackgroundColorToggleONInit(),false); //--- Set the initial frame color this.SetBorderColor(this.BorderColorInit(),false); //--- Send the test message to the journal Print(DFUN_ERR_LINE,TextByLanguage("Отмена","Cancel")); } //--- The mouse button released within the element means a click on the control else { //--- If this is a simple button, set the color for "The cursor is over the active area" status if(!this.Toggle()) this.SetBackgroundColor(this.BackgroundColorMouseOver(),false); //--- If this is the toggle button, else { //--- set the button status to the opposite one this.SetState(!this.State()); //--- set the background color for "The cursor is over the active area" status depending on whether the button is clicked or not this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleONMouseOver() : this.BackgroundColorMouseOver(),false); } //--- Send the test message to the journal Print(DFUN_ERR_LINE,TextByLanguage("Щелчок","Click")); //--- Set the frame color for "The cursor is over the active area" status this.SetBorderColor(this.BorderColorMouseOver(),false); } //--- Redraw the object this.Redraw(false); } //+------------------------------------------------------------------+
La lógica de cada método se detalla en los comentarios al código y esperamos que no requiera explicación.
Último manejador de eventos del ratón:
//+------------------------------------------------------------------+ //| Last mouse event handler | //+------------------------------------------------------------------+ void CButton::OnMouseEventPostProcessing(void) { ENUM_MOUSE_FORM_STATE state=GetMouseState(); switch(state) { //--- The cursor is outside the form, the mouse buttons are not clicked //--- The cursor is outside the form, any mouse button is clicked //--- The cursor is outside the form, the mouse wheel is being scrolled case MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED : case MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL : if(MouseEventLast()==MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED) { this.SetBackgroundColor(this.State() ? this.BackgroundColorToggleON() : this.BackgroundColorInit(),false); this.SetBorderColor(this.BorderColorInit(),false); this.m_mouse_event_last=ENUM_MOUSE_EVENT(state+MOUSE_EVENT_NO_EVENT); this.Redraw(false); } break; //--- The cursor is inside the form, the mouse buttons are not clicked case MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED : break; //--- The cursor is inside the form, any mouse button is clicked case MOUSE_FORM_STATE_INSIDE_FORM_PRESSED : break; //--- The cursor is inside the form, the mouse wheel is being scrolled case MOUSE_FORM_STATE_INSIDE_FORM_WHEEL : break; //--- The cursor is inside the active area, the mouse buttons are not clicked case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : break; //--- The cursor is inside the active area, any mouse button is clicked case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : break; //--- The cursor is inside the active area, the mouse wheel is being scrolled case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : break; //--- The cursor is inside the active area, left mouse button is released case MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED : break; //--- The cursor is within the window scrolling area, the mouse buttons are not clicked case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED : break; //--- The cursor is within the window scrolling area, any mouse button is clicked case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED : break; //--- The cursor is within the window scrolling area, the mouse wheel is being scrolled case MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL : break; //--- MOUSE_EVENT_NO_EVENT default: break; } } //+------------------------------------------------------------------+
El método solo procesa el estado cuando el cursor del ratón está fuera del formulario y el último evento de ratón es el cursor sobre el formulario y no hay botones del ratón presionados. El color de fondo del botón se escribirá con su color original, dependiendo de si el botón está pulsado o no (para un botón simple, su valor retornado por el método State() es siempre false).
Método que establece los colores para el estado "activado" de un botón-interruptor:
//+------------------------------------------------------------------+ //| Set the colors for the toggle element 'enabled' status | //+------------------------------------------------------------------+ void CButton::SetColorsToggleON(const color back,const color back_down,const color back_over,const bool set_init_color) { this.SetBackgroundColorToggleON(back,set_init_color); this.SetBackgroundColorToggleONMouseDown(back_down); this.SetBackgroundColorToggleONMouseOver(back_over); } //+------------------------------------------------------------------+
Los colores para los tres estados del botón son transmitidos al método, y se establecen en las variables correspondientes. En este caso, la bandera set_init_color permite determinar si el color original del fondo del botón debe guardarse o no.
Vamos a mejorar la clase de colección de elementos gráficos en el archivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.
Tenemos dos bloques de código idénticos en el método GetFormUnderCursor(), donde se realiza la búsqueda de los objetos vinculados al formulario y que además se encuentran bajo el cursor. Sacaremos este bloque de código a un método aparte para acortar el código del método y hacer más clara su lógica.
En la sección privada de la clase, declaramos este método y el método para procesar los eventos del formulario del que se ha quitado el cursor:
//--- Add (1) the standard graphical object and (2) the graphical element on canvas to the collection bool AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control); //--- Search for interaction objects CForm *SearchInteractObj(CForm *form,const int id,const long &lparam,const double &dparam,const string &sparam); //--- Return the pointer to the form located under the cursor CForm *GetFormUnderCursor(const int id, const long &lparam, const double &dparam, const string &sparam, ENUM_MOUSE_FORM_STATE &mouse_state, long &obj_ext_id, int &form_index); //--- Reset all interaction flags for all forms except the specified one void ResetAllInteractionExeptOne(CGCnvElement *form); //--- Post-processing of the former active form under the cursor void FormPostProcessing(void); //--- Add the element to the collection list bool AddCanvElmToCollection(CGCnvElement *element);
En todos los métodos encargados de crear diversos elementos gráficos, los métodos para establecer los colores en los que es necesario indicar la bandera para almacenar el color original, ya tienen estas banderas escritas en sus métodos de ajuste de color (transmitimos el valor true porque necesitamos almacenar el color original al crear un objeto). Todos estos cambios ya los hemos realizado en la clase, y no los analizaremos aquí para ahorrar espacio.
Como ejemplo, aquí tenemos un método que crea un objeto de formulario gráfico en el lienzo en el gráfico y la subventana especificados:
//--- Create a graphical form object on canvas on a specified chart and subwindow int CreateForm(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h, const color clr, const uchar opacity, const bool movable, const bool activity, const bool shadow=false, const bool redraw=false) { int id=this.m_list_all_canv_elm_obj.Total(); CForm *obj=new CForm(chart_id,subwindow,name,x,y,w,h); ENUM_ADD_OBJ_RET_CODE res=this.AddOrGetCanvElmToCollection(obj,id); if(res==ADD_OBJ_RET_CODE_ERROR) return WRONG_VALUE; obj.SetID(id); obj.SetActive(activity); obj.SetMovable(movable); obj.SetBackgroundColor(clr,true); obj.SetBorderColor(clr,true); obj.SetOpacity(opacity,false); obj.SetShadow(shadow); obj.DrawRectangle(0,0,obj.Width()-1,obj.Height()-1,obj.BorderColor(),obj.Opacity()); obj.Done(); obj.Erase(clr,opacity,redraw); return obj.ID(); }
Estos cambios, y otros similares, se han realizado en todos los métodos en los que existe el ajuste de color.
Método para encontrar los objetos de interacción:
//+------------------------------------------------------------------+ //| Search for interaction objects | //+------------------------------------------------------------------+ CForm *CGraphElementsCollection::SearchInteractObj(CForm *form,const int id,const long &lparam,const double &dparam,const string &sparam) { //--- If a non-empty pointer is passed if(form!=NULL) { //--- Create the list of interaction objects int total=form.CreateListInteractObj(); //--- In the loop by the created list for(int i=total-1;i>WRONG_VALUE;i--) { //--- get the next form object CForm *obj=form.GetInteractForm(i); //--- If the object is received and the mouse cursor is located above the object, return the pointer to the found object if(obj!=NULL && obj.MouseFormState(id,lparam,dparam,sparam)>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL) return obj; } } //--- Return the same pointer return form; } //+------------------------------------------------------------------+
En el último artículo, vimos la lógica de este método, pero allí este código formaba parte del método GetFormUnderCursor().
Ahora, en lugar de este bloque de código, simplemente insertaremos una llamada al mismo:
//+------------------------------------------------------------------+ //| Return the pointer to the form located under the cursor | //+------------------------------------------------------------------+ CForm *CGraphElementsCollection::GetFormUnderCursor(const int id, const long &lparam, const double &dparam, const string &sparam, ENUM_MOUSE_FORM_STATE &mouse_state, long &obj_ext_id, int &form_index) { //--- Set the ID of the extended standard graphical object to -1 //--- and the index of the anchor point managed by the form to -1 obj_ext_id=WRONG_VALUE; form_index=WRONG_VALUE; //--- Initialize the mouse status relative to the form mouse_state=MOUSE_FORM_STATE_NONE; //--- Declare the pointers to graphical element collection class objects CGCnvElement *elm=NULL; CForm *form=NULL; //--- Get the list of objects the interaction flag is set for (there should be only one object) CArrayObj *list=CSelect::ByGraphCanvElementProperty(GetListCanvElm(),CANV_ELEMENT_PROP_INTERACTION,true,EQUAL); //--- If managed to obtain the list and it is not empty, if(list!=NULL && list.Total()>0) { //--- Get the only graphical element there elm=list.At(0); //--- If the element is a form object or its descendants if(elm.TypeGraphElement()>=GRAPH_ELEMENT_TYPE_WF_BASE) { //--- Assign the pointer to the element for the form object pointer form=elm; //--- Get the mouse status relative to the form mouse_state=form.MouseFormState(id,lparam,dparam,sparam); //--- If the cursor is inside the form, if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL) { //--- Find the interaction object. //--- This will be either the found object or the same form form=this.SearchInteractObj(form,id,lparam,dparam,sparam); //--- Return the form object return form; } } } //--- If there is no a single form object with a specified interaction flag, //--- in the loop by all graphical element collection class objects int total=this.m_list_all_canv_elm_obj.Total(); for(int i=0;i<total;i++) { //--- get the next element elm=this.m_list_all_canv_elm_obj.At(i); if(elm==NULL) continue; //--- if the obtained element is a form object or its descendants if(elm.TypeGraphElement()>=GRAPH_ELEMENT_TYPE_WF_BASE) { //--- Assign the pointer to the element for the form object pointer form=elm; //--- Get the mouse status relative to the form mouse_state=form.MouseFormState(id,lparam,dparam,sparam); //--- If the cursor is within the form, return the pointer to the form if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL) { //--- Find the interaction object. //--- This will be either the found object or the same form form=this.SearchInteractObj(form,id,lparam,dparam,sparam); //--- Return the form object return form; } } } //--- If there is no a single form object from the collection list //--- Get the list of extended standard graphical objects list=this.GetListStdGraphObjectExt(); if(list!=NULL) { //--- in the loop by all extended standard graphical objects for(int i=0;i<list.Total();i++) { //--- get the next graphical object, CGStdGraphObj *obj_ext=list.At(i); if(obj_ext==NULL) continue; //--- get the object of its toolkit, CGStdGraphObjExtToolkit *toolkit=obj_ext.GetExtToolkit(); if(toolkit==NULL) continue; //--- handle the event of changing the chart for the current graphical object obj_ext.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam); //--- Get the total number of form objects created for the current graphical object total=toolkit.GetNumControlPointForms(); //--- In the loop by all form objects for(int j=0;j<total;j++) { //--- get the next form object, form=toolkit.GetControlPointForm(j); if(form==NULL) continue; //--- get the mouse status relative to the form mouse_state=form.MouseFormState(id,lparam,dparam,sparam); //--- If the cursor is inside the form, if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL) { //--- set the object ID and form index //--- and return the pointer to the form obj_ext_id=obj_ext.ObjectID(); form_index=j; return form; } } } } //--- Nothing is found - return NULL return NULL; } //+------------------------------------------------------------------+
Con este pequeño ajuste, el código del método ahora será más legible.
Método que procesa los eventos de ratón del antiguo formulario activo bajo el cursor:
//+------------------------------------------------------------------+ //| Post-processing of the former active form under the cursor | //+------------------------------------------------------------------+ void CGraphElementsCollection::FormPostProcessing(void) { //--- Get all the elements of the CForm type and above CArrayObj *list=GetList(CANV_ELEMENT_PROP_TYPE,GRAPH_ELEMENT_TYPE_FORM,EQUAL_OR_MORE); if(list==NULL) return; //--- In the loop by the list of received elements int total=list.Total(); for(int i=0;i<total;i++) { //--- get the pointer to the object CForm *obj=list.At(i); //--- if failed to get the pointer, move on to the next one in the list if(obj==NULL) continue; //--- Create the list of interaction objects and get their number int count=obj.CreateListInteractObj(); //--- In the loop by the obtained list for(int j=0;j<count;j++) { //--- get the next object CForm *elm=obj.GetInteractForm(j); if(elm==NULL) continue; //--- and call its method of handling mouse events elm.OnMouseEventPostProcessing(); } } } //+------------------------------------------------------------------+
Cuando alejamos el cursor de un objeto de formulario en un gráfico, ese objeto perderá el foco y se seleccionará automáticamente otro objeto debajo del cursor. O bien no habrá ningún objeto bajo el cursor, en cuyo caso se activarán todas las funciones desactivadas del gráfico (su desplazamiento, su menú contextual, etcétera). No obstante, a veces debemos procesar los movimientos de los objetos, como cuando queremos cambiar el color del cursor al alejarlo de un objeto, en nuestro caso, para restaurar su color original. De eso exactamente se encarga este método. El método itera en un ciclo por todos los elementos gráficos de tipo "formulario" y superiores, creando para cada uno de ellos una lista de objetos vinculados, y luego recorre esos objetos para llamar al método de gestión de eventos de ratón que comentamos anteriormente para cada objeto sucesivo. De esta forma, se procesará cada uno de los objetos de cada formulario del gráfico, pero solo si su último evento de ratón es el que queremos (el cursor se encuentra sobre el objeto y ningún botón del ratón está pulsado). Después de procesar el evento, asignaremos al último evento de ratón del objeto su estado actual: el cursor está fuera del formulario. De esta manera, solo se procesará un objeto, aquel del que se ha retirado el cursor por última vez, y solo se procesará una vez.
//--- If this is a mouse movement event and the movement flag is active, move the form, above which the cursor is located (if the pointer to it is valid) if(id==CHARTEVENT_MOUSE_MOVE && move) { if(form!=NULL) { //---... //---...
Tenemos que separarlos y entonces, si la bandera de movimiento está desactivada pero el ratón se está moviendo (con la bandera de retención activada), podremos detectar esta condición. Y si soltamos el botón del ratón, se registrará el evento MOUSE_FORM_STATE_NONE. Este evento solo aparece en este caso, ya que aún no hemos procesado tal situación; hablamos del evento de liberación del botón del ratón.
Por ello, necesitaremos separar la comprobación del evento de desplazamiento del cursor y la bandera de movimiento establecida. Y, en el caso de que la bandera de desplazamiento esté desactivada, deberemos definir el evento MOUSE_FORM_STATE_NONE y sustituirlo por el nuevo evento MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED.
Simplemente mostraremos un fragmento de código reelaborado de la forma que hemos descrito anteriormente. Aquí hemos separado los dos eventos para comprobarlos por separado:
if(id==CHARTEVENT_MOUSE_MOVE) { //--- If the cursor is above the form if(form!=NULL) { //--- If the move flag is set, shift the form following the cursor if(move) { //--- calculate the cursor movement relative to the form coordinate origin int x=this.m_mouse.CoordX()-form.OffsetX(); int y=this.m_mouse.CoordY()-form.OffsetY(); //--- get the width and height of the chart the form is located at int chart_width=(int)::ChartGetInteger(form.ChartID(),CHART_WIDTH_IN_PIXELS,form.SubWindow()); int chart_height=(int)::ChartGetInteger(form.ChartID(),CHART_HEIGHT_IN_PIXELS,form.SubWindow()); //--- If the form is not within an extended standard graphical object if(form_index==WRONG_VALUE) { //--- Adjust the calculated form coordinates if the form is out of the chart range if(x<0) x=0; if(x>chart_width-form.Width()) x=chart_width-form.Width(); if(y<0) y=0; if(y>chart_height-form.Height()) y=chart_height-form.Height(); //--- If the one-click trading panel is not present on the chart, if(!::ChartGetInteger(form.ChartID(),CHART_SHOW_ONE_CLICK)) { //--- calculate the form coordinates so that the form does not overlap with the one-click trading panel button if(y<17 && x<41) y=17; } //--- If the one-click trading panel is on the chart, else { //--- calculate the form coordinates so that the form does not overlap with the one-click trading panel if(y<80 && x<192) y=80; } } //--- If the form is included into the extended standard graphical object else { if(graph_obj_id>WRONG_VALUE) { //--- Get the list of objects by object ID (there should be one object) CArrayObj *list_ext=CSelect::ByGraphicStdObjectProperty(GetListStdGraphObjectExt(),GRAPH_OBJ_PROP_ID,0,graph_obj_id,EQUAL); //--- If managed to obtain the list and it is not empty, if(list_ext!=NULL && list_ext.Total()>0) { //--- get the graphical object from the list CGStdGraphObj *ext=list_ext.At(0); //--- If the pointer to the object has been received, if(ext!=NULL) { //--- get the object type ENUM_OBJECT type=ext.GraphObjectType(); //--- If the object is built using screen coordinates, set the coordinates to the object if(type==OBJ_LABEL || type==OBJ_BUTTON || type==OBJ_BITMAP_LABEL || type==OBJ_EDIT || type==OBJ_RECTANGLE_LABEL) { ext.SetXDistance(x); ext.SetYDistance(y); } //--- otherwise, if the object is built based on time/price coordinates, else { //--- calculate the shift from the form coordinate origin to its center int shift=(int)::ceil(form.Width()/2)+1; //--- If the form is located on one of the graphical object pivot points, if(form_index<ext.Pivots()) { //--- limit the form coordinates so that they do not move beyond the chart borders if(x+shift<0) x=-shift; if(x+shift>chart_width) x=chart_width-shift; if(y+shift<0) y=-shift; if(y+shift>chart_height) y=chart_height-shift; //--- set the calculated coordinates to the object ext.ChangeCoordsExtendedObj(x+shift,y+shift,form_index); } //--- If the form is central for managing all pivot points of a graphical object else { //--- Get screen coordinates of all object pivot points and write them to the m_data_pivot_point structure if(this.GetPivotPointCoordsAll(ext,m_data_pivot_point)) { //--- In the loop by the number of object pivot points, for(int i=0;i<(int)this.m_data_pivot_point.Size();i++) { //--- limit the screen coordinates of the current pivot point so that they do not move beyond the chart borders //--- By X coordinate if(x+shift-::fabs(this.m_data_pivot_point[i].ShiftX)<0) x=-shift+::fabs(m_data_pivot_point[i].ShiftX); if(x+shift+::fabs(this.m_data_pivot_point[i].ShiftX)>chart_width) x=chart_width-shift-::fabs(this.m_data_pivot_point[i].ShiftX); //--- By Y coordinate if(y+shift-::fabs(this.m_data_pivot_point[i].ShiftY)<0) y=-shift+::fabs(this.m_data_pivot_point[i].ShiftY); if(y+shift+::fabs(this.m_data_pivot_point[i].ShiftY)>chart_height) y=chart_height-shift-::fabs(this.m_data_pivot_point[i].ShiftY); //--- set the calculated coordinates to the current object pivot point ext.ChangeCoordsExtendedObj(x+shift+this.m_data_pivot_point[i].ShiftX,y+shift+this.m_data_pivot_point[i].ShiftY,i); } } } } } } } } //--- Move the form by the obtained coordinates if(form.IsMain()) form.Move(x,y,true); } //--- If the move flag is disabled else { //--- The undefined mouse status in mouse_state means releasing the left button //--- Assign the new mouse status to the variable if(mouse_state==MOUSE_FORM_STATE_NONE) mouse_state=MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED; //--- Handle moving the cursor mouse away from the graphical element this.FormPostProcessing(); } } }
y ahora somos capaces de "atrapar" el nuevo evento de ratón, siempre que la bandera de desplazamiento esté desactivada, y también procesar inmediatamente el evento de desplazamiento para el objeto de formulario activo.
Ahora, en el propio manejador de eventos OnChartEvent(), en los bloques de los manejadores de eventos de ratón, solo nos queda llamar el manejador de eventos de ratón para el objeto indicando el evento hallado anteriormente:
//--- If the cursor is above the form else { //--- If the button is still pressed and held on the chart, exit if(pressed_chart) { return; } //--- If the flag of holding the button on the form is not set yet if(!pressed_form) { pressed_chart=false; // The button is not pressed on the chart this.SetChartTools(form.ChartID(),false); //+---------------------------------------------------------------------------------------------+ //| 'The cursor is inside the form, no mouse buttons are clicked' event handler | //+---------------------------------------------------------------------------------------------+ if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED) { form.OnMouseEvent(MOUSE_EVENT_INSIDE_FORM_NOT_PRESSED,lparam,dparam,sparam); //--- If the cursor is above the form for managing the pivot point of an extended graphical object, if(graph_obj_id>WRONG_VALUE) { //--- get the object by its ID and by the chart ID CGStdGraphObj *graph_obj=this.GetStdGraphObjectExt(graph_obj_id,form.ChartID()); if(graph_obj!=NULL) { //--- Get the toolkit of an extended standard graphical object CGStdGraphObjExtToolkit *toolkit=graph_obj.GetExtToolkit(); if(toolkit!=NULL) { //--- Draw a point with a circle on the form and delete it on all other forms toolkit.DrawOneControlPoint(form); } } } } //+---------------------------------------------------------------------------------------------+ //| 'The cursor is inside the form, a mouse button is clicked (any)' event handler | //+---------------------------------------------------------------------------------------------+ if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_PRESSED) { this.SetChartTools(::ChartID(),false); //--- If the flag of holding the form is not set yet if(!pressed_form) { pressed_form=true; // set the flag of pressing on the form pressed_chart=false; // disable the flag of pressing on the form } form.OnMouseEvent(MOUSE_EVENT_INSIDE_FORM_PRESSED,lparam,dparam,sparam); } //+---------------------------------------------------------------------------------------------+ //| 'The cursor is inside the form, the mouse wheel is being scrolled' event handler | //+---------------------------------------------------------------------------------------------+ if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_WHEEL) { form.OnMouseEvent(MOUSE_EVENT_INSIDE_FORM_WHEEL,lparam,dparam,sparam); } //+---------------------------------------------------------------------------------------------+ //| 'The cursor is inside the active area, the mouse buttons are not clicked' event handler | //+---------------------------------------------------------------------------------------------+ if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED) { //--- Set the cursor shift relative to the form initial coordinates form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX()); form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY()); form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_NOT_PRESSED,lparam,dparam,sparam); //--- If the cursor is above the active area of the form for managing the pivot point of an extended graphical object, if(graph_obj_id>WRONG_VALUE) { //--- get the object by its ID and by the chart ID CGStdGraphObj *graph_obj=this.GetStdGraphObjectExt(graph_obj_id,form.ChartID()); if(graph_obj!=NULL) { //--- Get the toolkit of an extended standard graphical object CGStdGraphObjExtToolkit *toolkit=graph_obj.GetExtToolkit(); if(toolkit!=NULL) { //--- Draw a point with a circle on the form and delete it on all other forms toolkit.DrawOneControlPoint(form); } } } } //+---------------------------------------------------------------------------------------------+ //| 'The cursor is inside the active area, any mouse button is clicked' event handler | //+---------------------------------------------------------------------------------------------+ if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED && !move) { pressed_form=true; // the flag of holding the mouse button on the form //--- If the left mouse button is pressed if(this.m_mouse.IsPressedButtonLeft()) { //--- Set flags and form parameters move=true; // movement flag form.SetInteraction(true); // flag of the form interaction with the environment form.BringToTop(); // form on the background - above all others form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX()); // Cursor shift relative to the X coordinate form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY()); // Cursor shift relative to the Y coordinate this.ResetAllInteractionExeptOne(form); // Reset interaction flags for all forms except the current one //--- Get the maximum ZOrder long zmax=this.GetZOrderMax(); //--- If the maximum ZOrder has been received and the form's ZOrder is less than the maximum one or the maximum ZOrder of all forms is equal to zero if(zmax>WRONG_VALUE && (form.Zorder()<zmax || zmax==0)) { //--- If the form is not a control point for managing an extended standard graphical object, //--- set the form's ZOrder above all others if(form.Type()!=OBJECT_DE_TYPE_GFORM_CONTROL) this.SetZOrderMAX(form); } } form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_PRESSED,lparam,dparam,sparam); } //+---------------------------------------------------------------------------------------------+ //| 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler | //+---------------------------------------------------------------------------------------------+ if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL) { form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_WHEEL,lparam,dparam,sparam); } //+---------------------------------------------------------------------------------------------+ //| 'The cursor is inside the active area, the left mouse button is clicked' event handler | //+---------------------------------------------------------------------------------------------+ if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_RELEASED) { form.OnMouseEvent(MOUSE_EVENT_INSIDE_ACTIVE_AREA_RELEASED,lparam,dparam,sparam); } //+---------------------------------------------------------------------------------------------+ //| 'The cursor is inside the window scrolling area, no mouse buttons are clicked' event handler| //+---------------------------------------------------------------------------------------------+ if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED) { form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_NOT_PRESSED,lparam,dparam,sparam); } //+------------------------------------------------------------------------------------------------+ //| 'The cursor is inside the window scrolling area, a mouse button is clicked (any)' event handler| //+------------------------------------------------------------------------------------------------+ if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED) { form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_PRESSED,lparam,dparam,sparam); } //+--------------------------------------------------------------------------------------------------+ //| 'The cursor is inside the window scrolling area, the mouse wheel is being scrolled' event handler| //+--------------------------------------------------------------------------------------------------+ if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL) { form.OnMouseEvent(MOUSE_EVENT_INSIDE_SCROLL_AREA_WHEEL,lparam,dparam,sparam); } } } } }
Aquí también hemos añadido un nuevo manejador para procesar el evento de liberación del botón del ratón.
En el método que establece para el gráfico indicado las banderas de scrolling (con la ruleta del ratón) del gráfico, el menú contextual y la herramienta «retícula», comprobamos la bandera ya establecida, para así evitar poner constantemente el mismo estado que ya se ha establecido para el gráfico:
//+------------------------------------------------------------------+ //| Set the flags of scrolling a chart | //| context menu and crosshairs for the chart | //+------------------------------------------------------------------+ void CGraphElementsCollection::SetChartTools(const long chart_id,const bool flag) { if(::ChartGetInteger(chart_id,CHART_MOUSE_SCROLL)==flag) return; ::ChartSetInteger(chart_id,CHART_MOUSE_SCROLL,flag); ::ChartSetInteger(chart_id,CHART_CONTEXT_MENU,flag); ::ChartSetInteger(chart_id,CHART_CROSSHAIR_TOOL,flag); } //+------------------------------------------------------------------+
Estas son todas las mejoras de las clases de la biblioteca hasta ahora.
Simulación
Para la prueba, tomaremos el asesor del artículo anterior y lo guardaremos en la nueva carpeta \MQL5\Experts\TestDoEasy\Part110\ con el nuevo nombre TstDE110.mq5.
¿Cómo realizaremos la prueba? Dejaremos todos los objetos del asesor anterior "como están", solo cambiaremos un poco el tamaño y la posición del botón, y haremos que obtenga punteros a los objetos según su tipo, como prueba de que el método funciona.
En los parámetros del asesor, añadiremos otro: la bandera del botón-interruptor y cambiaremos los parámetros por defecto:
//--- input parameters sinput bool InpMovable = true; // Panel Movable flag sinput ENUM_INPUT_YES_NO InpAutoSize = INPUT_YES; // Panel Autosize sinput ENUM_AUTO_SIZE_MODE InpAutoSizeMode = AUTO_SIZE_MODE_GROW; // Panel Autosize mode sinput ENUM_BORDER_STYLE InpFrameStyle = BORDER_STYLE_SIMPLE; // Label border style sinput ENUM_ANCHOR_POINT InpTextAlign = ANCHOR_CENTER; // Label text align sinput ENUM_INPUT_YES_NO InpTextAutoSize = INPUT_NO; // Label autosize sinput ENUM_ANCHOR_POINT InpCheckAlign = ANCHOR_LEFT; // Check flag align sinput ENUM_ANCHOR_POINT InpCheckTextAlign = ANCHOR_LEFT; // Check label text align sinput ENUM_CHEK_STATE InpCheckState = CHEK_STATE_UNCHECKED; // Check flag state sinput ENUM_INPUT_YES_NO InpCheckAutoSize = INPUT_YES; // CheckBox autosize sinput ENUM_BORDER_STYLE InpCheckFrameStyle = BORDER_STYLE_NONE; // CheckBox border style sinput ENUM_ANCHOR_POINT InpButtonTextAlign = ANCHOR_CENTER; // Button text align sinput ENUM_INPUT_YES_NO InpButtonAutoSize = INPUT_YES; // Button autosize sinput ENUM_AUTO_SIZE_MODE InpButtonAutoSizeMode= AUTO_SIZE_MODE_GROW; // Button Autosize mode sinput ENUM_BORDER_STYLE InpButtonFrameStyle = BORDER_STYLE_NONE; // Button border style sinput bool InpButtonToggle = false; // Button toggle flag //--- global variables
El manejador OnInit() contendrá ahora el siguiente código:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set EA global variables ArrayResize(array_clr,2); // Array of gradient filling colors array_clr[0]=C'26,100,128'; // Original ≈Dark-azure color array_clr[1]=C'35,133,169'; // Lightened original color //--- Create the array with the current symbol and set it to be used in the library string array[1]={Symbol()}; engine.SetUsedSymbols(array); //--- Create the timeseries object for the current symbol and period, and show its description in the journal engine.SeriesCreate(Symbol(),Period()); engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions //--- Create WinForms Panel object CPanel *pnl=NULL; pnl=engine.CreateWFPanel("WFPanel",50,50,230,150,array_clr,200,true,true,false,-1,FRAME_STYLE_BEVEL,true,false); if(pnl!=NULL) { //--- Set Padding to 4 pnl.SetPaddingAll(4); //--- Set the flags of relocation, auto resizing and auto changing mode from the inputs pnl.SetMovable(InpMovable); pnl.SetAutoSize(InpAutoSize,false); pnl.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpAutoSizeMode,false); //--- In the loop, create 2 bound panel objects CPanel *obj=NULL; for(int i=0;i<2;i++) { //--- create the panel object with calculated coordinates, width of 90 and height of 40 CPanel *prev=pnl.GetElement(i-1); int xb=0, yb=0; int x=(prev==NULL ? xb : xb+prev.Width()+20); int y=0; if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_PANEL,x,y,90,40,C'0xCD,0xDA,0xD7',200,true,false)) { obj=pnl.GetElement(i); if(obj==NULL) continue; obj.SetBorderSizeAll(3); obj.SetBorderStyle(FRAME_STYLE_BEVEL); obj.SetBackgroundColor(obj.ChangeColorLightness(obj.BackgroundColor(),4*i),true); obj.SetForeColor(clrRed,true); //--- Calculate the width and height of the future text label object int w=obj.Width()-obj.BorderSizeLeft()-obj.BorderSizeRight(); int h=obj.Height()-obj.BorderSizeTop()-obj.BorderSizeBottom(); //--- Create a text label object obj.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LABEL,0,0,w,h,clrNONE,255,false,false); //--- Get the pointer to a newly created object CLabel *lbl=obj.GetElement(0); if(lbl!=NULL) { //--- If the object has an even or zero index in the list, set the default text color for it if(i % 2==0) lbl.SetForeColor(CLR_DEF_FORE_COLOR,true); //--- If the object index in the list is odd, set the object opacity to 127 else lbl.SetForeColorOpacity(127); //--- Set the font Black width type and //--- specify the text alignment from the EA settings lbl.SetFontBoldType(FW_TYPE_BLACK); lbl.SetTextAlign(InpTextAlign); lbl.SetAutoSize((bool)InpTextAutoSize,false); //--- For an object with an even or zero index, specify the Bid price for the text, otherwise - the Ask price of the symbol lbl.SetText(GetPrice(i % 2==0 ? SYMBOL_BID : SYMBOL_ASK)); //--- Set the frame width, type and color for a text label and update the modified object lbl.SetBorderSizeAll(1); lbl.SetBorderStyle((ENUM_FRAME_STYLE)InpFrameStyle); lbl.SetBorderColor(CLR_DEF_BORDER_COLOR,true); lbl.Update(true); } } } //--- Create the 'GroupBox' WinForms object CGroupBox *gbox=NULL; //--- Indent from attached panels by 6 pixels is a Y coordinate for GroupBox int w=pnl.GetUnderlay().Width(); int y=obj.BottomEdgeRelative()+6; //--- If the attached GroupBox object is created if(pnl.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,0,y,210,110,C'0x91,0xAA,0xAE',0,true,false)) { //--- get the pointer to the GroupBox object by its index in the list of bound GroupBox type objects gbox=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GROUPBOX,0); if(gbox!=NULL) { //--- set the "indented frame" type, the frame color matches the main panel background color, //--- while the text color is the background color of the last attached panel darkened by 1 gbox.SetBorderStyle(FRAME_STYLE_STAMP); gbox.SetBorderColor(pnl.BackgroundColor(),true); gbox.SetForeColor(gbox.ChangeColorLightness(obj.BackgroundColor(),-1),true); //--- Create the CheckBox object gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,2,10,50,20,clrNONE,255,true,false); //--- get the pointer to the CheckBox object by its index in the list of bound CheckBox type objects CCheckBox *cbox=gbox.GetElementByType(GRAPH_ELEMENT_TYPE_WF_CHECKBOX,0); //--- If CheckBox is created and the pointer to it is received if(cbox!=NULL) { //--- Set the CheckBox parameters from the EA inputs cbox.SetAutoSize((bool)InpCheckAutoSize,false); cbox.SetCheckAlign(InpCheckAlign); cbox.SetTextAlign(InpCheckTextAlign); //--- Set the displayed text, frame style and color, as well as checkbox status cbox.SetText("CheckBox"); cbox.SetBorderStyle((ENUM_FRAME_STYLE)InpCheckFrameStyle); cbox.SetBorderColor(CLR_DEF_BORDER_COLOR,true); cbox.SetChecked(true); cbox.SetCheckState((ENUM_CANV_ELEMENT_CHEK_STATE)InpCheckState); } //--- Create the RadioButton object gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,2,cbox.BottomEdgeRelative()+4,50,20,clrNONE,255,true,false); //--- get the pointer to the RadioButton object by its index in the list of bound RadioButton type objects CRadioButton *rbtn=gbox.GetElementByType(GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,0); //--- If RadioButton is created and the pointer to it is received if(rbtn!=NULL) { //--- Set the RadioButton parameters from the EA inputs rbtn.SetAutoSize((bool)InpCheckAutoSize,false); rbtn.SetCheckAlign(InpCheckAlign); rbtn.SetTextAlign(InpCheckTextAlign); //--- Set the displayed text, frame style and color, as well as checkbox status rbtn.SetText("RadioButton"); rbtn.SetBorderStyle((ENUM_FRAME_STYLE)InpCheckFrameStyle); rbtn.SetBorderColor(CLR_DEF_BORDER_COLOR,true); rbtn.SetChecked(true); } //--- Create the Button object gbox.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BUTTON,(int)fmax(rbtn.RightEdgeRelative(),cbox.RightEdgeRelative())+10,14,60,36,clrNONE,255,true,false); //--- get the pointer to the Button object by its index in the list of bound Button type objects CButton *butt=gbox.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BUTTON,0); //--- If Button is created and the pointer to it is received if(butt!=NULL) { //--- Set the Button parameters from the EA inputs butt.SetAutoSize((bool)InpButtonAutoSize,false); butt.SetAutoSizeMode((ENUM_CANV_ELEMENT_AUTO_SIZE_MODE)InpButtonAutoSizeMode,false); butt.SetTextAlign(InpButtonTextAlign); //--- Set the text color, as well as frame style and color butt.SetForeColor(butt.ChangeColorLightness(CLR_DEF_FORE_COLOR,2),true); butt.SetBorderStyle((ENUM_FRAME_STYLE)InpButtonFrameStyle); butt.SetBorderColor(butt.ChangeColorLightness(butt.BackgroundColor(),-10),true); //--- Set the 'toggle' mode depending on the settings butt.SetToggleFlag(InpButtonToggle); //--- Set the displayed text on the button depending on the 'toggle' flag if(butt.Toggle()) butt.SetText("Toggle-Button"); else butt.SetText("Button"); } } } //--- Redraw all objects according to their hierarchy pnl.Redraw(true); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
En esencia, todo sigue como estaba, pero hemos cambiado los nombres de los métodos renombrados en la biblioteca: en los métodos de establecimiento del color, ahora se transmiten banderas(true) para almacenar el color original, y cuando seleccionamos un modo de botón concreto, el nombre de ese modo se mostrará como texto en el botón.
Podrá encontrar todos los cambios realizados en el asesor en los archivos adjuntos al artículo.
Vamos a compilar el asesor y ejecutarlo en el gráfico:
Como podemos ver, todos los componentes visuales declarados de los objetos funcionan correctamente al interactuar con el ratón.
¿Qué es lo próximo?
En el próximo artículo seguiremos trabajando en la interactividad de los objetos de la biblioteca WinForms.
*Artículos de esta serie:
DoEasy. Controles (Parte 1): Primeros pasos
DoEasy. Elementos de control (Parte 2): Continuamos trabajando con la clase CPanel
DoEasy. Elementos de control (Parte 3): Creando controles vinculados
DoEasy. Elementos de control (Parte 4): Elemento de control "Panel", parámetros Padding y Dock
DoEasy. Elementos de control (Parte 5): Objeto básico WinForms, control «Panel», parámetro AutoSize
DoEasy. Elementos de control (Parte 6): Control «Panel», cambio automático del tamaño del contenedor según el contenido interno
DoEasy. Elementos de control (Parte 7): Elemento de control «etiqueta de texto»
DoEasy. Elementos de control (Parte 8): Objetos básicos WinForms por categorías, controles "GroupBox" y "CheckBox
DoEasy. Elementos de control (Parte 9): Reorganizando los métodos de los objetos WinForms, los controles "RadioButton" y "Button"
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/11173
- 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