DoEasy. Controls (Part 28): Bar styles in the ProgressBar control
Contents
Concept
Currently, the ProgressBar control created for the library has a single progress bar display style — a continuous line (Continuous). But such a control has two more display styles - segmented blocks (Blocks) and continuous scrolling of a block in an object (Marquee). The Blocks style is pretty clear (a continuous line is replaced with separately located blocks). The Marquee style can be used if the number of iterations that need to be visually displayed using the ProgressBar control is not known in advance. In this case, a single block equal to half the width of the progress bar will constantly scroll.
In addition to creating these two new styles, I will add a text to the progress bar to be displayed inside. The text itself will be represented by a regular object of the CLabel class of the library, and it will be bound not to the progress bar object, but to the underlay object, which is the base of the ProgressBar control. The text will be rendered inside a fully transparent text label object sized to the width and height of the progress bar. This object will always be in the foreground - above all objects in the ProgressBar control.
By default, the text will not be displayed on the progress bar, but it can always be added right during the execution of a library-based program. We just need to specify the output text and its attributes (font, size, font flags, color, opacity, etc).
Improving library classes
Before I start developing new styles for the ProgressBar control, let's simplify the constructors of the basic graphical element. After creating an object, I set the values of its properties in the protected and parametric constructors. In both constructors, this is a long list of more than a hundred different library graphical element parameters constantly added when new objects are created. Thus, we constantly have to write the setting of object properties twice - in the protected and in the parametric constructors of the CGCnvElement class. It would be logical to move the setting of all these properties into a separate initialization method. At the same time, if some properties should be set differently in different constructors, then they will simply be passed in the formal parameters of the new method and indicated in them.
In \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, add new properties to the object structure:
struct SData { //--- Object integer properties int id; // Element ID int type; // Graphical element type //---... //---... //--- int group; // Group the graphical element belongs to int tab_size_mode; // Tab size setting mode int tab_page_number; // Tab index number int tab_page_row; // Tab row index int tab_page_column; // Tab column index int progress_bar_minimum; // The lower bound of the range ProgressBar operates in int progress_bar_maximum; // The upper bound of the range ProgressBar operates in int progress_bar_step; // ProgressBar increment needed to redraw it int progress_bar_style; // ProgressBar style int progress_bar_value; // Current ProgressBar value from Min to Max int progress_bar_marquee_speed; // Progress bar animation speed in case of Marquee style //--- ulong tooltip_initial_delay; // Tooltip display delay ulong tooltip_auto_pop_delay; // Tooltip display duration ulong tooltip_reshow_delay; // One element new tooltip display delay bool tooltip_show_always; // Display a tooltip in inactive window int tooltip_icon; // Icon displayed in a tooltip bool tooltip_is_balloon; // Tooltip in the form of a "cloud" bool tooltip_use_fading; // Fade when showing/hiding a tooltip //--- 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 uchar descript[256]; // Graphical element description uchar tooltip_title[256]; // Element tooltip title uchar tooltip_text[256]; // Element tooltip text }; SData m_struct_obj; // Object structure
The structure of a graphical object is needed for the correct writing of the object properties to a file and reading them from the file during restoration. The properties now written into this structure have already been added to the object, but have not yet been added to the structure. Even if the fields of the structure do not match the properties of the object, then at this stage of development there is nothing to worry about. So far, I have not implemented saving the object to a file and reading its properties from the file. Subsequently, of course, I will save the properties of graphical objects to a file and read them from there. At that point, I will really need a complete match of the structure fields with the object properties.
In the private section of the class, declare a new method for initializing graphical element properties:
//--- 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); } //--- Initialize property values void Initialize(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int element_id,const int element_num, const int x,const int y,const int w,const int h, const string descript,const bool movable,const bool activity); public:
The properties of the created object will be passed to the method. The properties have different values in different constructors or are specified directly in the formal parameters of the constructors, i.e. are passed from outside. I will pass all such properties to the method via its parameters.
Remove a long list of setting object properties in each of the constructors replacing it with a call to the initialization method:
//+---------------------------------------------+ //| Parametric constructor | //+---------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main_obj,CGCnvElement *base_obj, const int element_id, const int element_num, const long chart_id, const int wnd_num, const string descript, 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.SetTypeElement(element_type); this.m_type=OBJECT_DE_TYPE_GELEMENT; this.m_element_main=main_obj; this.m_element_base=base_obj; this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND); this.m_name=this.CreateNameGraphElement(element_type); this.m_chart_id=(chart_id==NULL || chart_id==0 ? ::ChartID() : chart_id); this.m_subwindow=wnd_num; 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,x,y,w,h,redraw)) { this.Initialize(element_type,element_id,element_num,x,y,w,h,descript,movable,activity); this.SetVisibleFlag(false,false); } else { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj()); } } //+---------------------------------------------+ //| Protected constructor | //+---------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int wnd_num, const string descript, const int x, const int y, const int w, const int h) : m_shadow(false) { this.m_type=OBJECT_DE_TYPE_GELEMENT; this.m_element_main=main_obj; this.m_element_base=base_obj; this.m_chart_color_bg=(color)::ChartGetInteger((chart_id==NULL ? ::ChartID() : chart_id),CHART_COLOR_BACKGROUND); this.m_name=this.CreateNameGraphElement(element_type); 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(CLR_CANV_NULL,true); this.SetOpacity(0); 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,x,y,w,h,false)) { this.Initialize(element_type,0,0,x,y,w,h,descript,false,false); this.SetVisibleFlag(false,false); } else { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),"\"",this.TypeElementDescription(element_type),"\" ",this.NameObj()); } } //+------------------------------------------------------------------+
As we can see, the values inherent in each specific constructor are passed to the initialization method in both constructors.
In the new initialization method, move property setting removed from the class constructors:
//+---------------------------------------------+ //| Initialize the properties | //+---------------------------------------------+ void CGCnvElement::Initialize(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int element_id,const int element_num, const int x,const int y,const int w,const int h, const string descript,const bool movable,const bool activity) { 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_VISIBLE_AREA_X,0); // Visibility scope X coordinate this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_Y,0); // Visibility scope Y coordinate this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_WIDTH,w); // Visibility scope width this.SetProperty(CANV_ELEMENT_PROP_VISIBLE_AREA_HEIGHT,h); // Visibility scope height this.SetProperty(CANV_ELEMENT_PROP_DISPLAYED,true); // Non-hidden control display flag this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_STATE,CANV_ELEMENT_DISPLAY_STATE_NORMAL);// Control display state this.SetProperty(CANV_ELEMENT_PROP_DISPLAY_DURATION,DEF_CONTROL_PROCESS_DURATION); // Control display duration this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_X,0); // Control area X coordinate this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_Y,0); // Control area Y coordinate this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_WIDTH,0); // Control area width this.SetProperty(CANV_ELEMENT_PROP_CONTROL_AREA_HEIGHT,0); // Control area height this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_RIGHT,0); // Right scroll area X coordinate this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_RIGHT,0); // Right scroll area Y coordinate this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_RIGHT,0); // Right scroll area width this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_RIGHT,0); // Right scroll area height this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_X_BOTTOM,0); // Bottom scroll area X coordinate this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_Y_BOTTOM,0); // Bottom scroll area Y coordinate this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_WIDTH_BOTTOM,0); // Bottom scroll area width this.SetProperty(CANV_ELEMENT_PROP_SCROLL_AREA_HEIGHT_BOTTOM,0); // Bottom scroll area height this.SetProperty(CANV_ELEMENT_PROP_BORDER_LEFT_AREA_WIDTH,0); // Left edge area width this.SetProperty(CANV_ELEMENT_PROP_BORDER_BOTTOM_AREA_WIDTH,0); // Bottom edge area width this.SetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH,0); // Right edge area width this.SetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH,0); // Top edge area width //--- 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_STATE_ON,CLR_DEF_FORE_COLOR); // Text color of the control which is on this.SetProperty(CANV_ELEMENT_PROP_FORE_COLOR_STATE_ON_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_STATE_ON_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_STATE_ON,CLR_DEF_CONTROL_STD_BACK_COLOR_ON);// Background color of the control which is on this.SetProperty(CANV_ELEMENT_PROP_BACKGROUND_COLOR_STATE_ON_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_STATE_ON_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 this.SetProperty(CANV_ELEMENT_PROP_BUTTON_GROUP,false); // Button group flag this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_MULTI_COLUMN,false); // Horizontal display of columns in the ListBox control this.SetProperty(CANV_ELEMENT_PROP_LIST_BOX_COLUMN_WIDTH,0); // Width of each ListBox control column this.SetProperty(CANV_ELEMENT_PROP_TAB_MULTILINE,false); // Several lines of tabs in TabControl this.SetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT,CANV_ELEMENT_ALIGNMENT_TOP); // Location of tabs inside the control this.SetProperty(CANV_ELEMENT_PROP_ALIGNMENT,CANV_ELEMENT_ALIGNMENT_TOP); // Location of an object inside the control this.SetProperty(CANV_ELEMENT_PROP_TEXT,""); // Graphical element text this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,descript); // Graphical element description this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_FIXED_PANEL,0); // Panel that retains its size when the container is resized this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_FIXED,true); // Separator moveability flag this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_DISTANCE,50); // Distance from edge to separator this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_WIDTH,4); // Separator width this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_SPLITTER_ORIENTATION,0); // Separator location this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_COLLAPSED,false); // Flag for collapsed panel 1 this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL1_MIN_SIZE,25); // Panel 1 minimum size this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_COLLAPSED,false); // Flag for collapsed panel 1 this.SetProperty(CANV_ELEMENT_PROP_SPLIT_CONTAINER_PANEL2_MIN_SIZE,25); // Panel 2 minimum size this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,500); // Tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,5000); // Tooltip display duration this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,100); // One element new tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,false); // Display a tooltip in inactive window this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,CANV_ELEMENT_TOOLTIP_ICON_NONE); // Icon displayed in a tooltip this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,false); // Tooltip in the form of a "cloud" this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,true); // Fade when showing/hiding a tooltip this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,""); // Tooltip title for the element this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,""); // Tooltip text for the element this.SetProperty(CANV_ELEMENT_PROP_GROUP,0); // Group the graphical element belongs to this.SetProperty(CANV_ELEMENT_PROP_TAB_SIZE_MODE,CANV_ELEMENT_TAB_SIZE_MODE_NORMAL);// Tab size setting mode this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER,0); // Tab index number this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_ROW,0); // Tab row index this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_COLUMN,0); // Tab column index this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM,100); // The upper bound of the range ProgressBar operates in this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM,0); // The lower bound of the range ProgressBar operates in this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,10); // ProgressBar increment needed to redraw it this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE,CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS); // ProgressBar style this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,50); // Current ProgressBar value from Min to Max this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,10); // Progress bar animation speed in case of Marquee style } //+------------------------------------------------------------------+
Here we simply moved the strings removed from the class constructors. Property values that were different in different constructors are now passed and set to object properties through formal method parameters.
In the method creating an object structure, add setting values of the appropriate graphical element properties to the new structure fields:
//+---------------------------------------------+ //| 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.belong=(int)this.GetProperty(CANV_ELEMENT_PROP_BELONG); // Graphical element affiliation this.m_struct_obj.number=(int)this.GetProperty(CANV_ELEMENT_PROP_NUM); // Element ID in the list //---... //---... this.m_struct_obj.border_right_area_width=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH); // Right edge area width this.m_struct_obj.border_top_area_width=(int)this.GetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH); // Top edge area width //--- this.m_struct_obj.tooltip_initial_delay=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY); // Tooltip display delay this.m_struct_obj.tooltip_auto_pop_delay=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY); // Tooltip display duration this.m_struct_obj.tooltip_reshow_delay=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY); // One element new tooltip display delay this.m_struct_obj.tooltip_show_always=this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS); // Display a tooltip in inactive window this.m_struct_obj.tooltip_icon=(int)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON); // Icon displayed in the tooltip this.m_struct_obj.tooltip_is_balloon=(bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON); // Balloon tooltip this.m_struct_obj.tooltip_use_fading=(bool)this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING); // Fade when showing and hiding the tooltip //--- this.m_struct_obj.group=(int)this.GetProperty(CANV_ELEMENT_PROP_GROUP); // Group the graphical element belongs to this.m_struct_obj.tab_size_mode=(int)this.GetProperty(CANV_ELEMENT_PROP_TAB_SIZE_MODE); // Tab size setting mode this.m_struct_obj.tab_page_number=(int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER); // Tab index number this.m_struct_obj.tab_page_row=(int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_ROW); // Tab row index this.m_struct_obj.tab_page_column=(int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_COLUMN); // Tab column index this.m_struct_obj.progress_bar_maximum=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM); // The upper bound of the range ProgressBar operates in this.m_struct_obj.progress_bar_minimum=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM); // The lower bound of the range ProgressBar operates in this.m_struct_obj.progress_bar_step=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP); // ProgressBar increment needed to redraw it this.m_struct_obj.progress_bar_style=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE); // ProgressBar style this.m_struct_obj.progress_bar_value=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE); // Current ProgressBar value from Min to Max this.m_struct_obj.progress_bar_marquee_speed=(int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED);// Progress bar animation speed in case of Marquee style //--- 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 ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_DESCRIPTION),this.m_struct_obj.descript);// Graphical element description ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE),this.m_struct_obj.tooltip_title);// Tooltip title for the element ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT),this.m_struct_obj.tooltip_text); // Tooltip text for the element //--- 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; } //+------------------------------------------------------------------+
In the method that creates an object from a structure, implement setting object properties from the corresponding structure fields added in the current article:
//+---------------------------------------------+ //| 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_BELONG,this.m_struct_obj.belong); // Graphical element affiliation this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number); // Element index in the list //---... //---... this.SetProperty(CANV_ELEMENT_PROP_BORDER_RIGHT_AREA_WIDTH,this.m_struct_obj.border_right_area_width); // Right edge area width this.SetProperty(CANV_ELEMENT_PROP_BORDER_TOP_AREA_WIDTH,this.m_struct_obj.border_top_area_width); // Top edge area width this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_INITIAL_DELAY,this.m_struct_obj.tooltip_initial_delay); // Tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_AUTO_POP_DELAY,this.m_struct_obj.tooltip_auto_pop_delay);// Tooltip display duration this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,this.m_struct_obj.tooltip_reshow_delay);// One element new tooltip display delay this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_SHOW_ALWAYS,this.m_struct_obj.tooltip_show_always);// Display a tooltip in inactive window this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_ICON,this.m_struct_obj.tooltip_icon); // Icon displayed in a tooltip this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_IS_BALLOON,this.m_struct_obj.tooltip_is_balloon); // Balloon tooltip this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_USE_FADING,this.m_struct_obj.tooltip_use_fading); // Fade when showing/hiding a tooltip this.SetProperty(CANV_ELEMENT_PROP_GROUP,this.m_struct_obj.group); // Group the graphical element belongs to this.SetProperty(CANV_ELEMENT_PROP_TAB_SIZE_MODE,this.m_struct_obj.tab_size_mode); // Tab size setting mode this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER,this.m_struct_obj.tab_page_number); // Tab index number this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_ROW,this.m_struct_obj.tab_page_row); // Tab row index this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_COLUMN,this.m_struct_obj.tab_page_column); // Tab column index this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM,this.m_struct_obj.progress_bar_maximum);// The upper bound of the range ProgressBar operates in this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM,this.m_struct_obj.progress_bar_minimum);// The lower bound of the range ProgressBar operates in this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,this.m_struct_obj.progress_bar_step); // ProgressBar increment needed to redraw it this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE,this.m_struct_obj.progress_bar_style); // ProgressBar style this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,this.m_struct_obj.progress_bar_value); // Current ProgressBar value from Min to Max this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,this.m_struct_obj.progress_bar_marquee_speed); // Progress bar animation speed in case of Marquee style //--- 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 this.SetProperty(CANV_ELEMENT_PROP_DESCRIPTION,::CharArrayToString(this.m_struct_obj.descript));// Graphical element description this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TITLE,::CharArrayToString(this.m_struct_obj.tooltip_title));// Tooltip title for the element this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_TEXT,::CharArrayToString(this.m_struct_obj.tooltip_text)); // Tooltip text for the element } //+------------------------------------------------------------------+
Now the graphical element object will be correctly saved to and restored from a file.
A glare running along the progress bar can have the shape of a parallelepiped. This is a skewed quad with the top face vertices offset by 6 pixels from the bottom face vertices. For a small object, this offset is enough to make the object look fairly slanted. But as its height increases, the slope, set rigidly at six pixels, becomes indistinguishable. To correct the situation, we need to make the slope a relative value - the higher the object, the stronger the slope. So, to set the size of the offset of the vertices, we will use the height of the object - then visually the slope will be at 45 degrees.
In \MQL5\Include\DoEasy\Objects\Graph\WForms\GlareObj.mqh, in the method that draws the shape of the object glare in the form of a parallelogram, fix the initialization of the parameters of the vertex coordinates. Instead of the number 6, assign the value of the graphical object height:
//+------------------------------------------------------------------+ //| Draw the shape of the object glare as a parallelogram | //+------------------------------------------------------------------+ void CGlareObj::DrawFigureParallelogram(void) { int array_x[]={this.Height(),this.Width()-1,this.Width()-1-this.Height(),0}; int array_y[]={0,0,this.Height()-1,this.Height()-1}; CGCnvElement::DrawPolygonFill(array_x,array_y,this.m_color,this.OpacityDraw()); CGCnvElement::Update(); } //+------------------------------------------------------------------+
Now the vertices will be offset by the value of the height of the object and the slope will always be at about 45 degrees, regardless of the object height.
I can use different options to create a "Segmented Blocks" progress bar style — from directly creating each block to simply drawing them. I am going to do this as follows. I already have a completely filled progress bar, and I only need to erase the background of this object in those places where there are no blocks. In this case, I will get the appearance of a progress bar, consisting of segmented blocks. The disadvantage of this approach is that the location of each block should be calculated. The advantage of this approach is the simplicity of its implementation.
In the private section in \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\BarProgressBar.mqh, add the variables for storing segment parameters and declare the method for the object background segmentation:
//+------------------------------------------------------------------+ //| BarProgressBar object class of the ProgressBar control | //+------------------------------------------------------------------+ class CBarProgressBar : public CWinFormBase { private: int m_segment_s; // Segment countdown start int m_segment_x; // Last segment X coordinate int m_segment_w; // Segment width int m_segment_d; // Distance between segments //--- Segment the background void Segmentation(void); //--- (1) Set and (2) return a pause before displaying the effect void SetShowDelay(const long delay) { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,delay); } ulong ShowDelay(void) { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY); } //--- Initialize the properties void Initialize(void); protected: //--- Protected constructor with object type, chart ID and subwindow CBarProgressBar(const ENUM_GRAPH_ELEMENT_TYPE type, CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); public:
The declared variables will contain the initial parameters the location of the segments can be calculated from. The segment width will always be 3/4 of the height, and the distance between the segments will be calculated from the resulting segment width. This data will be recorded when the object is initialized, and then it can be used to calculate the location of any segment by its number. Since we are not drawing the segments themselves, but the spaces between them, the starting point will be the width of the first segment counted either from zero or from one. If the height of the progress bar is more than three pixels, then a one-pixel empty space should be drawn around the edge of the object on all sides. This will separate the segments from the outer edge of the bar, making them visually independent units. In this case, the indent of the first segment should start at one, which is a one-pixel empty space from the edge of the object to the first segment. If the height of the progress bar is three pixels or less, then we do not need to draw empty space - it will occupy the entire usable area of the object the segments are drawn on. In this case, the indent to the beginning of the first segment should be zero.
In the public section of the class, implement the method that returns the style of the progress bar and the methods that return the values of the variable parameters of the segments. Let's declare the methods for calculating the width of the segment and the distance between them. I will also need to redefine the virtual methods for clearing the object background:
public: //--- Set the (1) animation speed in case of Marquee style, (2) display style, (3) increment value and (4) the current value of the ProgressBar control void SetMarqueeAnimationSpeed(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MARQUEE_ANIM_SPEED,value); } void SetStyle(const ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE style) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE,style); } void SetStep(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,value); } void SetValue(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,value); } //--- Return the display style ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE Style(void) const { return (ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STYLE); } //--- Return (1) the X coordinate of the last segment, (2) segment width, (3) distance between segments and (4) segment countdown start int SegmentX(void) const { return this.m_segment_x; } int SegmentWidth(void) const { return this.m_segment_w; } int SegmentDistance(void) const { return this.m_segment_d; } int SegmentStart(void) const { return this.m_segment_s; } //--- Calculate (1) the segment width and (2) the distance between segments int CalculateSegmentWidth(void); int CalculateSegmentDistance(const int width); //--- Supported object properties (1) integer, (2) real and (3) string ones virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- 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); //--- Constructor CBarProgressBar(CGCnvElement *main_obj,CGCnvElement *base_obj, const long chart_id, const int subwindow, const string descript, const int x, const int y, const int w, const int h); //--- Timer virtual void OnTimer(void); }; //+------------------------------------------------------------------+
In the clearing methods, the element background is filled with its background color. Accordingly, after the filling, it is necessary to remove the background from the gaps between the segments. In these methods, we will call the method declared in the private section for segmenting the drawn object background. Since they are virtual, it is always these methods that will be called when calling the cleaning method if the object is a progress bar or its successor.
In the object properties initialization method, enter the default values for the variables that store the segmentation parameters. The last segment X coordinate and the countdown start will be equal to zero, while the segment width and the distance between them will be calculated at once:
//+---------------------------------------------+ //| Initialize the properties | //+---------------------------------------------+ void CBarProgressBar::Initialize(void) { this.SetPaddingAll(0); this.SetMarginAll(0); this.SetBorderSizeAll(0); this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true); this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true); this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true); this.SetShowDelay(2000); this.m_segment_x=0; this.m_segment_s=0; this.m_segment_w=this.CalculateSegmentWidth(); this.m_segment_d=this.CalculateSegmentDistance(this.m_segment_w); } //+------------------------------------------------------------------+
Let's consider the declared methods in details.
The method clearing the element filling it with color and opacity:
//+------------------------------------------------------------------+ //| Clear the element filling it with color and opacity | //+------------------------------------------------------------------+ void CBarProgressBar::Erase(const color colour,const uchar opacity,const bool redraw=false) { //--- Fill the element having the specified color and the redrawing flag CGCnvElement::EraseNoCrop(colour,opacity,false); //--- Segment the background this.Segmentation(); //--- If the object has a frame, draw it if(this.BorderStyle()!=FRAME_STYLE_NONE) this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),this.BorderStyle()); //--- Crop the excess and update the element with the specified redraw flag this.Crop(); this.Update(redraw); } //+------------------------------------------------------------------+
Here, the entire background of the element is first filled. The segmentation method is then called erasing the background in those places of the object where there should be no segments. If the object has a frame, then it is drawn. The parts of the object protruding beyond the parent container are cropped, and the background of the object is updated with the chart redrawing flag passed to the method.
The method that clears an element with a gradient fill:
//+---------------------------------------------+ //| Clear the element with a gradient fill | //+---------------------------------------------+ void CBarProgressBar::Erase(color &colors[],const uchar opacity,const bool vgradient,const bool cycle,const bool redraw=false) { //--- Fill the element having the specified color array and the redrawing flag CGCnvElement::EraseNoCrop(colors,opacity,vgradient,cycle,false); //--- Segment the background this.Segmentation(); //--- If the object has a frame, draw it if(this.BorderStyle()!=FRAME_STYLE_NONE) this.DrawFormFrame(this.BorderSizeTop(),this.BorderSizeBottom(),this.BorderSizeLeft(),this.BorderSizeRight(),this.BorderColor(),this.Opacity(),this.BorderStyle()); //--- Crop the excess and update the element with the specified redraw flag this.Crop(); this.Update(redraw); } //+------------------------------------------------------------------+
The logic of the method is identical to the logic of the above method, except that not a single color is passed to the method, but rather the color array of the gradient fill and its direction flag — vertical/horizontal and cyclic.
The method that calculates the segment width:
//+---------------------------------------------+ //| Calculate the segment width | //+---------------------------------------------+ int CBarProgressBar::CalculateSegmentWidth(void) { int w=(int)::ceil((this.Height()-2)/1.75); return(w>3 ? w : 3); } //+------------------------------------------------------------------+
The width of any segment should be 2/3 of its height. This method calculates the width of the segment based on the height of the progress bar object. Let's take the height of the object minus two pixels. More often, the height of the progress bar will be more than three pixels, which means that free space of one pixel will be drawn on each side of the progress bar along its perimeter, which will reduce the visible height of the segment by two pixels (one above and one below). The resulting height is divided by 1.75, which will give an aspect ratio of 3/4. If the resulting width is less than three pixels, then the width will be equal to three pixels, since segments that are too small in height look bad with a width of 1-2 pixels.
The method that calculates the distance between segments:
//+---------------------------------------------+ //| Calculate the distance between segments | //+---------------------------------------------+ int CBarProgressBar::CalculateSegmentDistance(const int width) { int d=(int)::ceil(width/6); return(d<1 ? 1 : d); } //+------------------------------------------------------------------+
All is simple here. The method receives the width the indent should be calculated from. The distance between the segments should be six times less than the width of the block, but not less than one pixel.
Background segmentation method:
//+---------------------------------------------+ //| Segment the background | //+---------------------------------------------+ void CBarProgressBar::Segmentation(void) { //--- If the drawing style is not "Segmented blocks", leave if(this.Style()!=CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS) return; //--- Reset the X coordinate of the segment this.m_segment_x=0; //--- Get the block width as 3/4 of its height int w=this.SegmentWidth(); //--- Get the distance between the segments (six times less than the block width) int d=this.SegmentDistance(); //--- Get the countdown start this.m_segment_s=w+(this.Height()>3 ? 1 : 0); //--- In the loop from the beginning of the countdown to the width of the progress bar with a step in the block width + indent between segments for(int i=this.SegmentStart();i<this.Width();i+=w+d) { //--- draw an empty fully transparent rectangle (erasing the element background) this.DrawRectangleFill(i,0,i+d-1,this.Height()-1,CLR_CANV_NULL,0); //--- Store the X coordinate of the segment this.m_segment_x=i; } //--- If the height of the progress line is more than three pixels, draw a completely transparent frame around the perimeter if(this.Height()>3) this.DrawRectangle(0,0,this.Width()-1,this.Height()-1,CLR_CANV_NULL,0); } //+------------------------------------------------------------------+
The method logic is fully described in the code comments. If the progress bar style is "Segmented blocks", then in a loop we go through the entire background of the object and erase the background where there should be no segments. If the height of the object is more than three pixels, then we additionally erase the background along the perimeter with a one-pixel wide border, which will make the segments separated from the edges of the progress bar.
In the ProgressBar object, add the ability to include a description displayed on the progress bar. By default, the description will not be displayed, but it is enough just to define its text in order to enable it. The description will be built by a CLabel object permanently attached to the object. Naturally, it will be possible to create as many such objects as you like and set them in the right places. But the progress bar description object will be created by default when the ProgressBar is created, and it will be easier to access using special methods.
In the \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ProgressBar.mqh CProgressBar class file, remove the variable for storing the number of skipped steps from the private section since there was no need for it:
//+------------------------------------------------------------------+ //| ArrowLeftRightBox object class of WForms controls | //+------------------------------------------------------------------+ class CProgressBar : public CContainer { private: int m_progress_bar_max; // Maximum progress bar width int m_value_by_max; // Value relative to Maximum int m_steps_skipped; // Number of skipped steps of increasing the width of the progress bar //--- Create a new graphical object
Also, in the private section, declare the variables to store text label properties with the progress bar description:
//+------------------------------------------------------------------+ //| ArrowLeftRightBox object class of WForms controls | //+------------------------------------------------------------------+ class CProgressBar : public CContainer { private: int m_progress_bar_max; // Maximum progress bar width int m_value_by_max; // Value relative to Maximum int m_progress_bar_text_x; // X coordinate of the text label with the description of the progress bar int m_progress_bar_text_y; // Y coordinate of the text label with the description of the progress bar color m_progress_bar_text_color; // Color of the text describing the progress bar uchar m_progress_bar_text_opacity; // Opacity of the text describing the progress bar string m_progress_bar_text; // Text describing the progress bar ENUM_FRAME_ANCHOR m_progress_bar_text_anchor; // Method for binding the text with a progress bar description //--- Create a new graphical object
We will need the variables to set and return the properties of the text label object, which acts as a description of the progress bar.
The data displayed in the description line can be either actual, displaying each step of the execution, or as a percentage of the already completed steps.
In the public section of the class, implement the methods returning Value as a text and as a percentage in numeric and text forms:
//--- (1) Set and (2) return the current value of the progress bar in the range from Min to Max as a number and (3) as a text void SetValue(const int value); int Value(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE); } string ValueDescription(void) const { return (string)this.Value(); } //--- Return the current progress bar value in the range from Min to Max as a percentage in the form of (1) a number and (2) a text double ValuePercent(void) const; string ValuePercentDescription(void) const { return ::DoubleToString(this.ValuePercent(),2)+"%"; } //--- (1) Set and (2) return the upper bound of the ProgressBar operating range
In order to get a simplified access to the glare object, declare the methods for set its properties, as well as the methods for getting the pointers to the created attached objects in the public section of the class, and declare the methods for handling the progress bar description object:
//--- (1) Set and (2) return the lower bound of the ProgressBar operating range void SetMinimum(const int value) { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM,value); } int Minimum(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MINIMUM); } //--- Set (1) style, (2) opacity and (3) color for the glare object void SetGlareStyle(const ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE style); void SetGlareOpacity(const uchar opacity); void SetGlareColor(const color clr); //--- Return the pointer to the (1) progress bar object, (2) glare object and (3) the text label object with the progress bar description CBarProgressBar *GetProgressBar(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0); } CGlareObj *GetGlareObj(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0); } CLabel *GetProgressDescriptionObj(void) { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_LABEL,0); } //--- Set the (1) text, (2) color //--- (3) opacity, (4) X, (5) Y coordinates, (6) font, (7) size and (8) font flags to the progress bar text label void SetBarDescriptionText(const string text,const bool redraw=false); void SetBarDescriptionColor(const color clr,const bool redraw=false,const bool set_init_color=false); void SetBarDescriptionOpacity(const uchar opacity,const bool redraw=false); void SetBarDescriptionX(const int x,const bool redraw=false); void SetBarDescriptionY(const int y,const bool redraw=false); void SetBarDescriptionFontName(const string font,const bool redraw=false); void SetBarDescriptionFontSize(const int size,const bool relative=false,const bool redraw=false); void SetBarDescriptionFontFlags(const uint flags,const bool redraw=false); //--- (1) hide and (2) display the progress bar text label void HideBarDescription(void); void ShowBarDescription(void); //--- Resets the progress bar values to the set minimum
In the class constructor, initialize declared variables with default values:
//+---------------------------------------------+ //| Initialize the element properties | //+---------------------------------------------+ void CProgressBar::Initialize(void) { this.SetBorderSizeAll(1); this.SetBorderStyle(FRAME_STYLE_SIMPLE); this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true); this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true); this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true); this.SetMarqueeAnimationSpeed(10); this.SetMaximum(100); this.SetMinimum(0); this.SetValue(50); this.SetStep(10); this.SetStyle(CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS); this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight(); this.m_value_by_max=this.Value()*100/this.Maximum(); this.m_progress_bar_text=""; this.m_progress_bar_text_x=1; this.m_progress_bar_text_y=0; this.m_progress_bar_text_color=CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR; this.m_progress_bar_text_opacity=255; this.m_progress_bar_text_anchor=FRAME_ANCHOR_LEFT_TOP; } //+------------------------------------------------------------------+
In the method that creates the progress bar object, a glare object is also created. Add and create the progress bar description object there as well:
//+---------------------------------------------+ //| Create the progress bar object | //+---------------------------------------------+ void CProgressBar::CreateProgressBar(void) { //--- Set the length of the progress bar equal to the object Value() //--- The height of the progress bar is set equal to the height of the object minus the top and bottom border sizes int w=this.CalculateProgressBarWidth(); int h=this.Height()-this.BorderSizeTop()-this.BorderSizeBottom(); if(h<1) h=1; //--- Create the progress bar object this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0,0,w,h,clrNONE,255,false,false); //--- Create a description of the progress bar, get the text label object and set the default parameters this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_LABEL,1,0,this.Width()-2,this.Height()-2,clrNONE,0,false,false); CLabel *obj=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_LABEL,0); if(obj!=NULL) { obj.SetText(this.m_progress_bar_text); obj.SetFontName(DEF_FONT); obj.SetFontSize(DEF_FONT_SIZE); obj.SetTextAnchor(FRAME_ANCHOR_LEFT_TOP); obj.SetTextAlign(ANCHOR_LEFT_UPPER); this.HideBarDescription(); } //--- Create the glare object this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0,0,w,h,CLR_CANV_NULL,0,true,false); //--- Add the current CProgressBar object to the list of active elements of the collection this.AddObjToListActiveElements(); } //+------------------------------------------------------------------+
Immediately after creating the text label, get the pointer to this object and set some properties for it: text, font name, font size, anchoring method and text alignment, and then hide the created object. The remaining parameters of the object can be obtained later by obtaining the pointer to this object using the GetProgressDescriptionObj() method, and then setting the necessary properties to the already received object.
If we try to create the progress bar object with a height of less than one pixel, this will not work since the object cannot be created with zero size. So let's introduce a check for the zero size and adjust it in case the check is positive.
In the method creating a new graphical object, add the code block for creating a text label object:
//+---------------------------------------------+ //| Create a new graphical object | //+---------------------------------------------+ CGCnvElement *CProgressBar::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string descript, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *element=NULL; switch(type) { case GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR : element=new CBarProgressBar(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ : element=new CGlareObj(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; case GRAPH_ELEMENT_TYPE_WF_LABEL : element=new CLabel(this.GetMain(),this.GetObject(),this.ChartID(),this.SubWindow(),descript,x,y,w,h); break; default: break; } if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type)); return element; } //+------------------------------------------------------------------+
Now the ProgressBar object can create bound objects of the Progress Bar, Glare and Text Label types.
The Progress Bar object is the active control handled in the timer. At the moment, the timer is handling a glare that runs along the progress bar. The progress bar displayed in the Marquee style is to be handled in the timer as well. In this mode, the progress bar has a fixed width, equal to half the width of the entire object, and constantly scrolls inside the ProgressBar object, thereby showing the progress of the process, the number of iterations of which is not known in advance.
Let's add the following handling of the progress bar to the object timer handler:
//+---------------------------------------------+ //| Timer | //+---------------------------------------------+ void CProgressBar::OnTimer(void) { CBarProgressBar *bar=this.GetProgressBar(); if(bar!=NULL) { if(bar.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE) { int x=bar.CoordX()+8; if(x>this.RightEdge()) x=this.CoordX()-bar.Width(); bar.Move(x,bar.CoordY()); bar.Redraw(true); } else bar.OnTimer(); } } //+------------------------------------------------------------------+
Here, if the progress bar display style is "Continuous scrolling", then we get the X coordinate the object needs to be shifted to. At the moment, we add an offset of 8 pixels to the current X coordinate and shift the progress bar to the new coordinate horizontally. We first check that the progress bar object is completely outside its container on the right - in this case, we set its X coordinate so that the object is at the value of its width on the left beyond the left edge of its container. The Redraw() method, in addition to redrawing the object, performs one more function - it cuts off the parts of the object that protrude beyond the edges of the container. We have created a complete cycle of moving the progress bar inside its container, and at the same time, the parts that protrude beyond the edges are cut off.
If the object has a different rendering style, then we call the timer of the progress bar object to display the glare object.
Since we now have a progress bar drawing style of "Segmented Blocks", we need to refine the method that sets the progress bar value. Currently, this method, after setting the value, immediately resizes the object according to the new value. However, since segmented blocks should not be drawn pixel-by-pixel, but block-by-block, we need to understand how many blocks can fit on the set width of the progress bar. If the width is sufficient to draw a new segment, then the object changes its width already set in its property. If the width is insufficient (the current drawing segment will be incomplete and cropped), then we need to subtract the width of one segment from the set width of the object and set exactly this width for the progress bar. Only the penultimate segment with all the previous ones will be drawn in that case. Thus, the object width will always change only when all segments can be fully drawn without cropping them, which will give us a block-by-block change in the visible width of the progress bar. In this case, the Value will always be the one that was passed to the object. In other words, we only visually change the width of the object discretely, and the Value in it is always the one that is set for it.
I will implement the improvements described above to the method that sets the current value of the progress bar:
//+---------------------------------------------+ //| Set the current value of the progress bar | //+---------------------------------------------+ void CProgressBar::SetValue(const int value) { //--- Correct the value passed to the method and set it to the object property int v=(value<this.Minimum() ? this.Minimum() : value>this.Maximum() ? this.Maximum() : value); this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,v); //--- Get the progress bar object CBarProgressBar *bar=this.GetProgressBar(); if(bar!=NULL) { //--- Set 'value' for the progress bar bar.SetValue(v); //--- Calculate the width of the progress bar object int w=this.CalculateProgressBarWidth(); //--- If the calculated width is greater than the maximum possible value, set the maximum width if(w>this.m_progress_bar_max) w=this.m_progress_bar_max; //--- If the width is less than 1, then if(w<1) { //--- hide the progress bar and redraw the chart to display changes immediately bar.Hide(); ::ChartRedraw(bar.ChartID()); } //--- If the width value is not less than 1 else { //--- If the progress bar is hidden, display it and if(!bar.IsVisible()) bar.Show(); //--- If the style of the progress bar is "Continuous line", change the width of the object to the calculated value if(this.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS) bar.Resize(w,bar.Height(),true); //--- Otherwise, if the progress bar style is "Segmented blocks" else if(this.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS) { //--- Segment width including indent int wd=bar.SegmentWidth()+bar.SegmentDistance(); //--- The number of segments that fit the width of the progress bar int num=w/(wd>0 ? wd : 1); //--- The X coordinate of the last segment calculated from the beginning of the segment count int wx=bar.SegmentStart()+num*wd; //--- If the calculated width of the progress bar is less than the coordinate of the last segment, and this is not the last segment, //--- set the width of the progress bar equal to the penultimate segment coordinate if(w<wx-bar.SegmentDistance() && w<this.m_progress_bar_max) w=wx-wd+bar.SegmentDistance(); //--- If the calculated width of the progress bar is less than the coordinate of the last segment, or it is not the last segment, if(w<wx-bar.SegmentDistance() || w==this.m_progress_bar_max) //--- change the size of the progress bar in accordance with the received and adjusted width bar.Resize(w,bar.Height(),true); } } //--- If the progress bar description text is set, if(this.m_progress_bar_text!="") { //--- get the object description of the progress bar and bring it to the foreground CLabel *obj=this.GetProgressDescriptionObj(); if(obj!=NULL) obj.BringToTop(); } } } //+------------------------------------------------------------------+
The method logic is described in the code comments. The description text also needs to be handled: each time you change the value of the progress bar, you may need to change the description as well. Therefore, this object is always brought to the foreground if text is set for it.
The method that calculates the width of the progress bar needs to be improved so that it returns half the size of the ProgressBar control if the progress bar style is set to "Continuous scrolling":
//+---------------------------------------------+ //| Calculate the width of the progress bar | //+---------------------------------------------+ int CProgressBar::CalculateProgressBarWidth(void) { this.m_value_by_max=this.Value()*100/this.Maximum(); return(this.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE ? this.m_progress_bar_max/2 : this.m_progress_bar_max*this.m_value_by_max/100); } //+------------------------------------------------------------------+
Now the method will return the correct size of the scrollbar for any of its display styles.
In the method that sets the new width, you need to assign the width set in the object also for the text label object, which is used to display the description of the progress bar:
//+---------------------------------------------+ //| Set a new width | //+---------------------------------------------+ bool CProgressBar::SetWidth(const int width) { if(!CGCnvElement::SetWidth(width)) return false; this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight(); CBarProgressBar *bar=this.GetProgressBar(); if(bar==NULL) return false; int w=this.CalculateProgressBarWidth(); bar.SetWidth(w); CLabel *lbl=this.GetProgressDescriptionObj(); if(lbl!=NULL) lbl.SetWidth(w); return true; } //+------------------------------------------------------------------+
If the size of the text label object is not adjusted to the size of the progress bar, then text written to the apparent size of the progress bar may not fit in an object whose width is smaller than it visually appears. This will cause the text to simply be cropped. Therefore, it is necessary that the text label and the progress bar have the same dimensions.
Let's look at the auxiliary methods that serve to facilitate access to the properties of objects bound to the ProgressBar control.
The method that sets the style for the glare object:
//+---------------------------------------------+ //| Set the glare object style | //+---------------------------------------------+ void CProgressBar::SetGlareStyle(const ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE style) { CGlareObj *obj=this.GetGlareObj(); if(obj==NULL) return; obj.SetVisualEffectStyle(style); } //+------------------------------------------------------------------+
Here we get the pointer to the glare object and set the style to the resulting object.
The method that sets opacity for a glare object:
//+---------------------------------------------+ //| Set the opacity of the highlight object | //+---------------------------------------------+ void CProgressBar::SetGlareOpacity(const uchar opacity) { CGlareObj *obj=this.GetGlareObj(); if(obj==NULL) return; obj.SetOpacity(opacity); } //+------------------------------------------------------------------+
Get the pointer to the glare object and set the opacity value to the resulting object.
The method that sets the color for the glare object:
//+---------------------------------------------+ //| Set the color for the glare object | //+---------------------------------------------+ void CProgressBar::SetGlareColor(const color clr) { CGlareObj *obj=this.GetGlareObj(); if(obj==NULL) return; obj.SetColor(clr); } //+------------------------------------------------------------------+
Get the pointer to the glare object and set the color value passed to the method to the resulting object.
The method that sets a text to the text label of the progress bar:
//+------------------------------------------------------------------+ //| Set a text to the text label of the progress bar | //+------------------------------------------------------------------+ void CProgressBar::SetBarDescriptionText(const string text,const bool redraw=false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) return; this.m_progress_bar_text=text; obj.SetText(text); obj.Erase(false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } //+------------------------------------------------------------------+
Here we get the pointer to the text label object. Set the text, passed to the method, to the m_progress_bar_text variable. Then set that text to the object. Since the text will immediately be displayed on the object, in order to avoid overlapping one text on another, the background and everything that was drawn on it should be erased. Next, display the text and update the element.
The method that sets the text color of the progress bar to the text label:
//+------------------------------------------------------------------+ //| Set the text color of the progress bar to the text label | //+------------------------------------------------------------------+ void CProgressBar::SetBarDescriptionColor(const color clr,const bool redraw=false,const bool set_init_color=false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) return; this.m_progress_bar_text_color=clr; obj.SetForeColor(clr,false); obj.Erase(false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } //+------------------------------------------------------------------+
Get the pointer to the text label object. Set the color, passed to the method, to the m_progress_bar_text_color variable. Next, set the color for the object as the text color. Since the text will be displayed immediately, in order to avoid overlapping one text on another, the background of the text label is erased. Further on, the text is displayed and the element is updated.
The method that sets opacity to the progress bar text label:
//+---------------------------------------------+ //| Set opacity to the progress bar text label | //+---------------------------------------------+ void CProgressBar::SetBarDescriptionOpacity(const uchar opacity,const bool redraw=false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) return; this.m_progress_bar_text_opacity=opacity; obj.Erase(false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } //+------------------------------------------------------------------+
The logic of the method is identical to the above, but we set the opacity value to the corresponding variable and object.
The method that sets the X coordinate to the text label of the progress bar:
//+------------------------------------------------------------------+ //| Set the X coordinate to the text label of the progress bar | //+------------------------------------------------------------------+ void CProgressBar::SetBarDescriptionX(const int x,const bool redraw=false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) return; this.m_progress_bar_text_x=x; obj.Erase(false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } //+------------------------------------------------------------------+
The method that sets the Y coordinate to the text label of the progress bar:
//+------------------------------------------------------------------+ //| Set the Y coordinate to the text label of the progress bar | //+------------------------------------------------------------------+ void CProgressBar::SetBarDescriptionY(const int y,const bool redraw=false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) return; this.m_progress_bar_text_y=y; obj.Erase(false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } //+------------------------------------------------------------------+
The method that sets the font in the text label of the progress bar:
//+------------------------------------------------------------------+ //| Set the font in the text label of the progress bar | //+------------------------------------------------------------------+ void CProgressBar::SetBarDescriptionFontName(const string font,const bool redraw=false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) return; obj.SetFontName(font); obj.Erase(false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } //+------------------------------------------------------------------+
The method that sets the font size to the progress bar text label:
//+------------------------------------------------------------------+ //| Set the font size to the progress bar text label | //+------------------------------------------------------------------+ void CProgressBar::SetBarDescriptionFontSize(const int size,const bool relative=false,const bool redraw=false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) return; obj.SetFontSize(size,relative); obj.Erase(false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } //+------------------------------------------------------------------+
The method that sets the font flags in the text label of the progress bar:
//+------------------------------------------------------------------+ //| Set the font flags in the text label of the progress bar | //+------------------------------------------------------------------+ void CProgressBar::SetBarDescriptionFontFlags(const uint flags,const bool redraw=false) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) return; obj.SetFontFlags(flags); obj.Erase(false); obj.Text(this.m_progress_bar_text_x,this.m_progress_bar_text_y,this.m_progress_bar_text,this.m_progress_bar_text_color,this.m_progress_bar_text_opacity,this.m_progress_bar_text_anchor); obj.Update(redraw); } //+------------------------------------------------------------------+
All the methods shown above have the same logic and allow us to quickly set the desired text and font parameters to the progress bar description (to the text label). The changes are immediately displayed on the screen.
If we need to hide the description of the progress bar, then in addition to hiding it, the object needs to set its non-display flag, since it is constantly brought to the foreground, which causes it to be displayed. If, after hiding the object, we set the non-display flag for it, then the object will not be redrawn until it receives the display flag again.
The method hiding the text label of the progress bar:
//+---------------------------------------------+ //| Hide the progress bar text label | //+---------------------------------------------+ void CProgressBar::HideBarDescription(void) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) return; obj.SetDisplayed(false); obj.Hide(); } //+------------------------------------------------------------------+
Here we get a text label object with a description of the progress bar, set the non-display flag for it and hide the object.
The method that displays the text label of the progress bar:
//+---------------------------------------------+ //| Display a text label for the progress bar | //+---------------------------------------------+ void CProgressBar::ShowBarDescription(void) { CLabel *obj=this.GetProgressDescriptionObj(); if(obj==NULL) return; obj.SetDisplayed(true); obj.Show(); } //+------------------------------------------------------------------+
Here we get a text label object with a description of the progress bar, set the display flag for it and display the object.
The method returning the current progress bar value in the range from Min to Max as a percentage:
//+---------------------------------------------+ //| Return the current value of the progress bar| //| in the range from Min to Max as a percentage| //+---------------------------------------------+ double CProgressBar::ValuePercent(void) const { double range=this.Maximum()-this.Minimum(); return(this.Value()*100.0/(range>0 ? range : 1)); } //+------------------------------------------------------------------+
If we need to get the Value of the progress bar as a percentage of the range from Min to Max, then this method will calculate and return the value of how many percent of the specified range have already been processed. 100% is the difference between the maximum and minimum values set in the ProgressBar object.
Let's check the results.
Test
To perform the test, I will use the EA from the previous article and save it in \MQL5\Experts\TestDoEasy\Part128\ as TestDoEasy128.mq5.
Create a progress bar style enumeration for the English language and user language compilation versions:
//--- enumerations by compilation language #ifdef COMPILE_EN enum ENUM_AUTO_SIZE_MODE { AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW, // Grow AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK // Grow and Shrink }; enum ENUM_BORDER_STYLE { BORDER_STYLE_NONE=FRAME_STYLE_NONE, // None BORDER_STYLE_SIMPLE=FRAME_STYLE_SIMPLE, // Simple BORDER_STYLE_FLAT=FRAME_STYLE_FLAT, // Flat BORDER_STYLE_BEVEL=FRAME_STYLE_BEVEL, // Embossed (bevel) BORDER_STYLE_STAMP=FRAME_STYLE_STAMP, // Embossed (stamp) }; enum ENUM_CHEK_STATE { CHEK_STATE_UNCHECKED=CANV_ELEMENT_CHEK_STATE_UNCHECKED, // Unchecked CHEK_STATE_CHECKED=CANV_ELEMENT_CHEK_STATE_CHECKED, // Checked CHEK_STATE_INDETERMINATE=CANV_ELEMENT_CHEK_STATE_INDETERMINATE, // Indeterminate }; enum ENUM_ELEMENT_ALIGNMENT { ELEMENT_ALIGNMENT_TOP=CANV_ELEMENT_ALIGNMENT_TOP, // Top ELEMENT_ALIGNMENT_BOTTOM=CANV_ELEMENT_ALIGNMENT_BOTTOM, // Bottom ELEMENT_ALIGNMENT_LEFT=CANV_ELEMENT_ALIGNMENT_LEFT, // Left ELEMENT_ALIGNMENT_RIGHT=CANV_ELEMENT_ALIGNMENT_RIGHT, // Right }; enum ENUM_ELEMENT_TAB_SIZE_MODE { ELEMENT_TAB_SIZE_MODE_NORMAL=CANV_ELEMENT_TAB_SIZE_MODE_NORMAL, // Fit to tab title text width ELEMENT_TAB_SIZE_MODE_FIXED=CANV_ELEMENT_TAB_SIZE_MODE_FIXED, // Fixed size ELEMENT_TAB_SIZE_MODE_FILL=CANV_ELEMENT_TAB_SIZE_MODE_FILL, // Fit TabControl Size }; enum ENUM_ELEMENT_PROGRESS_BAR_STYLE { ELEMENT_PROGRESS_BAR_STYLE_BLOCKS=CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS, // Blocks ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS=CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS, // Continuous ELEMENT_PROGRESS_BAR_STYLE_MARQUEE=CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE, // Marquee }; #else enum ENUM_AUTO_SIZE_MODE { AUTO_SIZE_MODE_GROW=CANV_ELEMENT_AUTO_SIZE_MODE_GROW, // Increase only AUTO_SIZE_MODE_GROW_SHRINK=CANV_ELEMENT_AUTO_SIZE_MODE_GROW_SHRINK // Increase and decrease }; enum ENUM_BORDER_STYLE { BORDER_STYLE_NONE=FRAME_STYLE_NONE, // No frame BORDER_STYLE_SIMPLE=FRAME_STYLE_SIMPLE, // Simple frame BORDER_STYLE_FLAT=FRAME_STYLE_FLAT, // Flat frame BORDER_STYLE_BEVEL=FRAME_STYLE_BEVEL, // Embossed (convex) BORDER_STYLE_STAMP=FRAME_STYLE_STAMP, // Embossed (concave) }; enum ENUM_CHEK_STATE { CHEK_STATE_UNCHECKED=CANV_ELEMENT_CHEK_STATE_UNCHECKED, // Unchecked CHEK_STATE_CHECKED=CANV_ELEMENT_CHEK_STATE_CHECKED, // Checked CHEK_STATE_INDETERMINATE=CANV_ELEMENT_CHEK_STATE_INDETERMINATE, // Undefined }; enum ENUM_ELEMENT_ALIGNMENT { ELEMENT_ALIGNMENT_TOP=CANV_ELEMENT_ALIGNMENT_TOP, // Top ELEMENT_ALIGNMENT_BOTTOM=CANV_ELEMENT_ALIGNMENT_BOTTOM, // Bottom ELEMENT_ALIGNMENT_LEFT=CANV_ELEMENT_ALIGNMENT_LEFT, // Left ELEMENT_ALIGNMENT_RIGHT=CANV_ELEMENT_ALIGNMENT_RIGHT, // Right }; enum ENUM_ELEMENT_TAB_SIZE_MODE { ELEMENT_TAB_SIZE_MODE_NORMAL=CANV_ELEMENT_TAB_SIZE_MODE_NORMAL, // By tab title width ELEMENT_TAB_SIZE_MODE_FIXED=CANV_ELEMENT_TAB_SIZE_MODE_FIXED, // Fixed size ELEMENT_TAB_SIZE_MODE_FILL=CANV_ELEMENT_TAB_SIZE_MODE_FILL, // By TabControl size }; enum ENUM_ELEMENT_PROGRESS_BAR_STYLE { ELEMENT_PROGRESS_BAR_STYLE_BLOCKS=CANV_ELEMENT_PROGRESS_BAR_STYLE_BLOCKS, // Segmented blocks ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS=CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS, // Continuous bar ELEMENT_PROGRESS_BAR_STYLE_MARQUEE=CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE, // Continuous scrolling }; #endif //--- input parameters
Let's add two new parameters to the inputs — progress bar style and the flag for displaying progress bar values in percent:
//--- 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 = true ; // Button toggle flag sinput bool InpButtListMSelect = false; // ButtonListBox Button MultiSelect flag sinput bool InpListBoxMColumn = true; // ListBox MultiColumn flag sinput bool InpTabCtrlMultiline = false; // Tab Control Multiline flag sinput ENUM_ELEMENT_ALIGNMENT InpHeaderAlignment = ELEMENT_ALIGNMENT_TOP; // TabHeader Alignment sinput ENUM_ELEMENT_TAB_SIZE_MODE InpTabPageSizeMode = ELEMENT_TAB_SIZE_MODE_FILL; // TabHeader Size Mode sinput int InpTabControlX = 10; // TabControl X coord sinput int InpTabControlY = 20; // TabControl Y coord sinput ENUM_CANV_ELEMENT_TOOLTIP_ICON InpTooltipIcon = CANV_ELEMENT_TOOLTIP_ICON_NONE; // Tooltip Icon sinput string InpTooltipTitle = ""; // Tooltip Title sinput ENUM_ELEMENT_PROGRESS_BAR_STYLE InpProgressBarStyle= ELEMENT_PROGRESS_BAR_STYLE_BLOCKS; // Progress Bar Style sinput bool InpProgressBarPercent= false; // Show progress bar values as a percentage //--- global variables
In the EA's OnInit() handler when creating the ProgressBar control, set the style to the progress bar from the variable in the settings and set the parameters to describe the progress bar:
//--- If this is the first tab and the second panel if(n==0 && j==1) { //--- Create the ProgressBar control on it if(split_container.CreateNewElement(j,GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,4,4,100,12,clrNONE,255,false,false)) { CProgressBar *progress_bar=split_container.GetPanelElementByType(j,GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,0); if(progress_bar!=NULL) { //--- Set the style of the progress bar specified in the EA settings progress_bar.SetStyle((ENUM_CANV_ELEMENT_PROGRESS_BAR_STYLE)InpProgressBarStyle); //--- Set the parameters for describing the progress bar progress_bar.SetBarDescriptionText("Progress Bar "); progress_bar.SetBarDescriptionColor(panel.BackgroundColor()); progress_bar.SetBarDescriptionOpacity(255); progress_bar.SetBarDescriptionY(-2); } } }
In the loop of displaying and redrawing all panels, display the description when the progress bar style is "Continuous Line". At each iteration of the loop of incrementing the Value of the progress bar, display the Values in the description depending on the settings - as a percentage or in completed steps. Upon the loop completion, write in the description of the progress bar a message about the completion of the increment loop with the font changed to bold.
To set the parameters of the glare object, I will use the object quick access methods:
//--- Display and redraw all created panels for(int i=0;i<FORMS_TOTAL;i++) { //--- Get the panel object pnl=engine.GetWFPanel("WinForms Panel"+(string)i); if(pnl!=NULL) { //--- display and redraw the panel pnl.Show(); pnl.Redraw(true); //--- Get the TabControl object from the panel CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0); //--- Get the SplitContainer object from the first tab of the TabControl object CSplitContainer *sc=tc.GetTabElementByType(0,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,0); //--- Get the second panel from the SplitContainer object CSplitContainerPanel *scp=sc.GetPanel(1); //--- Get the ProgressBar object from the received panel CProgressBar *pb=scp.GetElementByType(GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,0); //--- Wait for 1/10 of a second Sleep(100); //--- Get the width of the ProgressBar object int w=pb.Width(); //--- In the loop, increase the width of the ProgressBar by 180 pixels with a delay of 1/50 for(int n=0;n<180;n++) { Sleep(20); pb.Resize(w+n,pb.Height(),true); } //--- Set the values for PerformStep of the ProgressBar object pb.SetValuesForProcessing(0,350,1,0); //--- Reset ProgressBar to minimum pb.ResetProgressBar(); //--- If the style of the progress bar is "Continuous line", display the progress bar description if(pb.Style()==CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS) pb.ShowBarDescription(); //--- Wait for 1/5 second Sleep(200); //--- If the style of the progress bar is not "Continuous scrolling" if(pb.Style()!=CANV_ELEMENT_PROGRESS_BAR_STYLE_MARQUEE) { //--- In the loop from the minimum to the maximum value of ProgressBar for(int n=0;n<=pb.Maximum();n++) { //--- call the method for increasing the progress bar by a given step with a wait of 1/5 second pb.PerformStep(); //--- Set the number of completed steps in the description of the progress bar pb.SetBarDescriptionText("Progress Bar, pass: "+(InpProgressBarPercent ? pb.ValuePercentDescription() : pb.ValueDescription())); Sleep(20); } } //--- Wait for 1/2 second, set the description font type to Bold and write a completion message on the progress bar Sleep(500); pb.SetBarDescriptionFontFlags(FW_BOLD); pb.SetBarDescriptionText("Progress Bar: Done"); //--- Set the glare object type - rectangle, opacity 40, color - white pb.SetGlareStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE); pb.SetGlareOpacity(40); pb.SetGlareColor(clrWhite); } } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Compile the EA and launch it on the chart:
As we can see, all the declared modes work well.
What's next?
In the next article, I will start developing the TrackBar control.
*Previous articles within the series:
DoEasy. Controls (Part 26): Finalizing the ToolTip WinForms object and moving on to ProgressBar development
DoEasy. Controls (Part 27): Working on ProgressBar WinForms object
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/11823
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use