Discussion of article "Graphical Interfaces X: Time control, List of checkboxes control and table sorting (build 6)" - page 2

 

However, it seems to me that the recent changes regarding specifying coordinates by offset from the edge of the form are not very friendly to the end user. If earlier the user could safely use two options for specifying the location of an object in the window, now one of them has become more complicated. I've had things fall apart like this all over the screen.

  1. Previously the user could specify the x,y coordinate of the object as an offset from any edge of the window-form. To do this, it was enough to specify the initial coordinate of this window plus/minus the offset in pixels. Now he is forced to start only from the left edge of the form - reducing the possibilities to specify the exact coordinate or offset of the coordinate in pixels from any edge of the form or object.
  2. If earlier the user could without questioning the size of the object from which he calculates the offset, calmly place a new object with an offset of the required size from any edge of the parent object, now he will be forced to recalculate the coordinates of the new object every time he changes the size of the object from which the new object is built with an offset.
    Example:
    Button1 - its x coordinate is 4 pixels away from the left edge of the form window. It is set as follows: x=m_window.X()+4; The size of button1 is 43 pixels wide. Its right edge can be obtained as follows: m_butt1.X2();
    Button2 - its coordinate is 2 pixels away from the right edge of button1. It is set as follows: x=m_butt1.X2()+2; The size of button2 is 24 pixels wide. Its right edge can be obtained as follows: m_butt2.X2();
    Button3 - its coordinate is 4 pixels away from the right edge of button2. It is set as follows: x=m_butt2.X2()+4; The size of button3 is 18 pixels wide. Its right edge can be obtained as follows: m_butt3.X2();
    The vertical separating bar is 4 pixels away from the right edge of button3: x=m_butt3.X2()+4;
    So we can easily change the size of any of the three buttons, and all of them will be located at the offset we set, and the separating bar will be neatly placed at the offset we need from button3.
  3. Now let's see what we have to do when building the interface according to the new rules:
    Button1: x_gap1=4;
    Button2: x_gap2=4+m_butt1.XSize()+2;
    Button3: x_gap3=4+m_butt1.XSize()+2+m_butt2.XSize()+4;
    R-Strip: x_gap4=4+m_butt1.XSize()+2+m_butt2.XSize()+4+m_butt3.XSize()+4;
    Here it is suggested to specify their X2() coordinate instead of specifying object sizes, but that's not the case - everything falls apart at once (I, for example, broke my head while I guessed that Y2() doesn't point to the right Y-coordinate location).

Let's see: I set the menu coordinates with an indent of 1 pixel horizontally and with an indent on the size of the "header" of the main window. The menu height is 18 pixels.

//--- Creating the main window menu
   x=m_window_main.X()+1;
   y=m_window_main.Y()+m_window_main.CaptionHeight();
   h=18;

The menu is in its place:


Now I need to put the table offset from the bottom edge of the menu. If the CaptionHeight() of the form window is 18 pixels, and the height of the menu is also 18 pixels, then the vertical offset for the table should be 36 pixels. There are two options: either we sit with a calculator and calculate all the required offsets (and recalculate them again in case of subsequent adjustment with size changes), or we try to get them. We choose the second one, of course, and try it: for the table y_gap should be equal to the height of the main window title plus the height of the menu: m_window_main.CaptionHeight()+m_menu_main.YSize();

   //--- Character list table
   x=2;
   y=m_window_main.CaptionHeight()+m_menu_main.YSize()+1;
   w=140;

Compile, get:


The tab is in place. But. Shouldn't the Y2() value of the menu in this case coincide with the one we calculated? After all, it is more convenient not to collect the sizes of all the objects following each other, but just get their coordinates, right? Let's try it - change the coordinate calculation to its obtaining:

   //--- Character list table
   x=2;
   //y=m_window_main.CaptionHeight()+m_menu_main.YSize()+1;
   y=m_menu_main.Y2()+1;
   w=140;

Compile, get:


Shouldn't Y2() return the same Y2 we calculated earlier ? m_window_main.CaptionHeight()+m_menu_main.YSize()+1;

So it shouldn't - now we don't specify coordinates with offset from any edge of the window, but only offset, and only from top or left. Good. Remove from the obtained Y2() coordinate the Y() coordinate of the top of the form window to get the value of the offset from the top of the form window:

   //--- Character list table
   x=2;
   //y=m_window_main.CaptionHeight()+m_menu_main.YSize()+1;
   y=m_menu_main.Y2()-m_window_main.Y()+1;
   w=140;

Compile and get what we want:


So, for twenty objects that are two pixels apart from each other, we should always add their sizes plus the offset from each other to get the right offset for the last object? After all, Y2() of the nineteenth object (from which you could previously build the twentieth object only by adding an offset of two points) now does not give us a starting point, and returns not what is expected.... and we have to add up the sizes of all objects to get the required offset or for each object before its creation we have to calculate its offset based on its Y2() coordinate and the initial Y() coordinate of the form window.

I don't understand it. Anatoly, please give me a solution. What am I doing wrong? Why is it like this at all? Why do you think it is more friendly now? If earlier you could simply specify the offset from any edge of the form window, from any object in this window, or even from the edge of the chart in general to build the required object, now you have to do a tambourine dance to calculate its coordinates before building it? Is it better and more convenient to write more code and different calculations?

I understand that you are the author and you know better. But still you let others use it. I (my opinion only) think that this innovation turned out to be the only bad decision to introduce it as an update - it became worse. It didn't have the easy interface building capabilities that it had before.

I apologise if I offended you (it used to be that messengers with bad news had their heads chopped off).

 
Artyom Trishkin:

...

I'm sorry if I offended you.

I'll reply a little later, but before I do, I'd like to clarify what you mean by this phrase in relation to me. What is "bad news" and who is a "messenger" in this context? :)

 
Anatoli Kazharski:

I will answer a little later, but before that, I would like to clarify what you wanted to say with this phrase about me. What is "bad news" and who is a "messenger" in this context? :)

:)

The bad news I called my attitude to the innovation that you can no longer take any arbitrary coordinate as the object coordinate reference point, but only the left and top edges of the form (I added a recalculation of coordinates in local - unnecessary calculations).... And the messenger is me, of course, since I didn't like it and said so :)

 
Artyom Trishkin:

:)

The bad news I called my attitude to the innovation that now you can't take any arbitrary coordinate as the coordinate reference point of the object, but only the left and top edges of the form (I've added a recalculation of coordinates in local - unnecessary calculations).... Well the messenger is me of course, since I didn't like it and said so :)

So there you have it. Good. Because I thought I had misunderstood. )

Here's my response:

Artyom Trishkin:

However, I think that the recent changes regarding specifying coordinates by offset from the form edge are not very friendly to the end user. If earlier the user could safely use two options of specifying the location of the object in the window, now one of them has become more complicated. I've had things fall apart-split all over the screen like that.

...

Now let's see what we have to do when building the interface according to the new rules:

Button1: x_gap1=4;
Button2: x_gap2=4+m_butt1.XSize()+2;
Button3: x_gap3=4+m_butt1.XSize()+2+m_butt2.XSize()+4;
R Strip: x_gap4=4+m_butt1.XSize()+2+m_butt2.XSize()+4+m_butt3.XSize()+4;

...

Instead of specifying the sizes of the objects, you'd be tempted to specify their X2() coordinate, but that's not the case - everything falls apart at once (I, for example, broke my head until I realised that Y2() doesn't point to the right location of the Y-coordinate).

It's very simple. Previously, you had to pass absolute coordinates to the methods of creating elements. They had to be calculated relative to the leftmost point of the form to which the element is attached.

Here is an example of what we had to do earlier:

//+------------------------------------------------------------------+
//|| Creates a button|
//+------------------------------------------------------------------+
bool CProgram::CreateSimpleButton(const int x_gap,const int y_gap,const string button_text)
  {
//--- Save the pointer to the form
   m_simple_button.WindowPointer(m_window);
//--- Coordinates
   int x=m_window.X()+x_gap;
   int y=m_window.Y()+y_gap;

//--- Set properties before creation
   m_simple_button.ButtonXSize(100);
//--- Creating a button
   if(!m_simple_button.CreateSimpleButton(m_chart_id,m_subwin,button_text,x,y))
      return(false);
//--- Add a pointer to the element in the base
   CWndContainer::AddToElementsArray(0,m_simple_button);
   return(true);
  }

//---

Now you don't need to calculate anything, as it is enough to pass relative coordinates.

An example of what needs to be done now:

//+------------------------------------------------------------------+
//|| Creates a button|
//+------------------------------------------------------------------+
bool CProgram::CreateSimpleButton(const int x_gap,const int y_gap,const string button_text)
  {
//--- Pass the panel object
   m_simple_button.WindowPointer(m_window);
//--- Set properties before creation
   m_simple_button.ButtonXSize(100);
//--- Creating a button
   if(!m_simple_button.CreateSimpleButton(m_chart_id,m_subwin,button_text,x_gap,y_gap))
      return(false);
//--- Add a pointer to the element in the base
   CWndContainer::AddToElementsArray(0,m_simple_button);
   return(true);
  }

//---

In your examples above, you're calculating absolute coordinates. Stupid decision followed by wrong conclusions hastily put in public.

The CElement ::X(), CElement ::X2(), CElement ::Y() and CElement::Y2() methods return absolute coordinates of element borders. And it was easy to understand it just by outputting these values to the terminal log, if there are any doubts.

Below is an example of calculating the indentation(relative coordinate) relative to some element. There are two buttons. The X (relative) coordinate for the second button is calculated relative to the right border of the first button.

//+------------------------------------------------------------------+
//|| Creates the GUI of the programme |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- Creating a panel
   if(!CreateWindow("EXPERT PANEL"))
      return(false);
   if(!CreateSimpleButton1(7,25,"BUTTON 1"))
      return(false);
   if(!CreateSimpleButton2(m_simple_button1.X2()-m_window.X()+5,25,"BUTTON 2"))
      return(false);
//---
   return(true);
  }

//---

As a result, the second button will be set with an indentation of 5 pixels from the right border of the first button:

//---

By changing the width or X-coordinate of the first button, the position of the second button will be automatically adjusted.

So unnecessary calculations are only in your example quoted above.

 
Anatoli Kazharski:

//+------------------------------------------------------------------+
//|| Creates the GUI of the programme |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- Creating a panel
   if(!CreateWindow("EXPERT PANEL"))
      return(false);
   if(!CreateSimpleButton1(7,25,"BUTTON 1"))
      return(false);
   if(!CreateSimpleButton2(m_simple_button1.X2()-m_window.X()+5,25,"BUTTON 2"))
      return(false);
//---
   return(true);
  }

//---

As a result, the second button will be set with an indentation of 5 pixels from the right border of the first button:

//---

By changing the width or X-coordinate of the first button, the position of the second button will be automatically adjusted.

So the extra calculations are only in your example quoted above.

Here... It just became not very clear:

if(!CreateSimpleButton2(m_simple_button1.X2()-m_window.X()+5,25,"BUTTON 2"))

I have to put this recalculation in my functions not to write this recalculation everywhere when specifying the coordinates of a newly created object, but to continue writing as it was before - it is clearer to see the relation of one object to another. It's a matter of taste and preferences of everyone, of course...

By the way, the file navigator falls apart even when specifying coordinates as you say. But maybe it is only me. I'm going to look for the reason.

Screenshots of the MetaTrader trading platform

EURUSD, D1, 2016.12.12

MetaQuotes Software Corp., MetaTrader 5, Demo

EURUSD, D1, 2016.12.12, MetaQuotes Software Corp., MetaTrader 5, Demo


 

Anatoly, it looks like you forgot to make edits to FileNavigator.mqh in the

//+------------------------------------------------------------------+
//| Creates a tree list|
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\folder_w10.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\text_file_w10.bmp"
//---
bool CFileNavigator::CreateTreeView(void)
  {
//--- Save the window pointer
   m_treeview.WindowPointer(m_wnd);
//--- Set properties
   m_treeview.Id(CElement::Id());
   m_treeview.ResizeListAreaMode(true);
   m_treeview.TreeViewAreaWidth(m_treeview_area_width);
   m_treeview.ContentAreaWidth(m_content_area_width);
   m_treeview.AutoXResizeMode(CElement::AutoXResizeMode());
   m_treeview.AutoXResizeRightOffset(CElement::AutoXResizeRightOffset());
   m_treeview.AnchorRightWindowSide(m_anchor_right_window_side);
   m_treeview.AnchorBottomWindowSide(m_anchor_bottom_window_side);
//--- Form arrays of the tree list
   int items_total=::ArraySize(m_g_item_text);
   for(int i=0; i<items_total; i++)
     {
      //--- Define the picture for the item (folder/file)
      string icon_path=(m_g_is_folder[i])? m_folder_icon : m_file_icon;
      //--- If this is a folder, delete the last character ('\') in the string
      if(m_g_is_folder[i])
         m_g_item_text[i]=::StringSubstr(m_g_item_text[i],0,::StringLen(m_g_item_text[i])-1);
      //--- Add an item to the tree list
      m_treeview.AddItem(i,m_g_prev_node_list_index[i],m_g_item_text[i],icon_path,m_g_item_index[i],
                         m_g_node_level[i],m_g_prev_node_item_index[i],m_g_items_total[i],m_g_folders_total[i],false,m_g_is_folder[i]);
     }
//--- Create a tree list
   if(!m_treeview.CreateTreeView(m_chart_id,m_subwin,m_x-1,m_y+m_address_bar_y_size-1))
      return(false);
//--- Save the dimensions of the navigator
   CElement::XSize(m_treeview.XSize());
   CElement::YSize(m_treeview.YSize()+m_address_bar_y_size);
   return(true);
  }
//+------------------------------------------------------------------+

This is where you need to add it:

//--- Create a tree list
   if(!m_treeview.CreateTreeView(m_chart_id,m_subwin,m_x-m_wnd.X()-1,m_y-m_wnd.Y()+m_address_bar_y_size-1))
      return(false);
 
Artyom Trishkin:

...

By the way, the file navigator crashes even when specifying coordinates as you say. But maybe it's just me. I went to look for the reason.

I haven't reproduced it myself:

 
Anatoli Kazharski:

I haven't reproduced it myself:

Check FileNavigator.mqh in the latest update.
 
Artyom Trishkin:
Check FileNavigator.mqh in the latest update.
That is the latest one.
 
Anatoli Kazharski:
That is the latest one.

I mean on the site in the article - maybe there is an old one nailed to the updates. I take the file from the latest update, downloaded from the site from the bottom of the article from the zip.

Here, I made the corrections as I wrote above:

MetaTrader trading platform screenshots

EURUSD, W1, 2016.12.12

MetaQuotes Software Corp., MetaTrader 5, Demo

EURUSD, W1, 2016.12.12, MetaQuotes Software Corp., MetaTrader 5, Demo