Обсуждение статьи "Графические интерфейсы X: Элемент "Время", элемент "Список из чекбоксов" и сортировка таблицы (build 6)" - страница 2

 

Всё же, как мне кажется, последние изменения относительно указания координат по смещению от края формы не очень дружественны к конечному пользователю. Если раньше пользователь мог спокойно использовать два варианта указания места расположения объекта в окне, то теперь один из них усложнился. У меня так всё развалилось-разлетелось по всему экрану.

  1. Пользователь ранее мог задать координату объекта x,y как смещение от любого края окна-формы. Для этого достаточно было указать начальную координату этого окна плюс/минус смещение в пикселях. Теперь он вынужден отталкиваться только от левого края формы - урезание возможностей задать точную координату либо смещение координаты в пикселях от любого края формы или объекта.
  2. Если ранее пользователь мог не задаваясь вопросами о размерах объекта, от которого он отсчитывает смещение, спокойно помещать новый объект со смещением на нужную величину от любого края объекта-родителя, то теперь он будет вынужден пересчитывать координаты нового объекта каждый раз, когда он изменит размер объекта, от которого со смещением строится новый объект.
    Пример:
    Кнопка1 - её координата х отстоит от левого края окна-формы на 4 пикселя. Задаётся так: x=m_window.X()+4; Размер кнопки1 43 пикселя по ширине. Её правый край можно получить так: m_butt1.X2();
    Кнопка2 - её координата отстоит от правого края кнопки1 на 2 пикселя. Задаётся так: x=m_butt1.X2()+2; Размер кнопки2 24 пикселя по ширине. Её правый край можно получить так: m_butt2.X2();
    Кнопка3 - её координата отстоит от правого края кнопки2 на 4 пикселя. Задаётся так: x=m_butt2.X2()+4; Размер кнопки3 18 пикселей по ширине. Её правый край можно получить так: m_butt3.X2();
    Вертикальная разделительная полоса отстоит от правого края кнопки3 на 4 пикселя: x=m_butt3.X2()+4;
    Таким образом мы можем спокойно изменять размеры любой из трёх кнопок, и все они дружно будут располагаться друг от друга на заданном нами смещении, и разделительная полоса аккуратненько будет стоять на нужном нам смещении от кнопки3.
  3. Теперь посмотрим что придётся проделать при построении интерфейса по новым правилам:
    Кнопка1: x_gap1=4;
    Кнопка2: x_gap2=4+m_butt1.XSize()+2;
    Кнопка3: x_gap3=4+m_butt1.XSize()+2+m_butt2.XSize()+4;
    РПолоса: x_gap4=4+m_butt1.XSize()+2+m_butt2.XSize()+4+m_butt3.XSize()+4;
    Тут напрашивается вместо указания размеров объектов указывать их координату X2(), но не тут-то было - сразу же всё распадается (я, например, пока догадался, что Y2() не указывает на нужную точку расположения Y-координаты, всю голову себе сломал.)

Смотрим: задаю координаты меню с отступом в 1 пиксель по горизонтали и с отступом на размер "шапки" главного окна. Высота меню 18 пикселей.

//--- Создание меню главного окна
   x=m_window_main.X()+1;
   y=m_window_main.Y()+m_window_main.CaptionHeight();
   h=18;

Меню встаёт на своё место:


Теперь мне нужно поставить таблицу со смещением от нижнего края меню. Если CaptionHeight() окна-формы равен 18 пикселям, размер меню по высоте тоже 18 пикселей, то смещение для таблицы по вертикали должно быть 36 пикселей. Есть два варианта: либо сидим с калькулятором и высчитываем все нужные смещения (а при последующей подгонке с изменениями размеров, заново пересчитываем), либо пытаемся их получить. Выбираем, естественно второе, и пробуем: для таблицы y_gap должен быть равен высоте заголовка главного окна плюс высота меню: m_window_main.CaptionHeight()+m_menu_main.YSize();

   //--- Таблица списка символов
   x=2;
   y=m_window_main.CaptionHeight()+m_menu_main.YSize()+1;
   w=140;

Компилируем, получаем:


Табличка встала на место. Но... А разве значение Y2() меню не должно в данном случае совпадать с тем, что мы высчитали? Ведь удобнее не собирать подряд размеры всех объектов, идущих друг за другом, а просто получать их координаты, верно же? Пробуем - меняем расчёт координаты на её получение:

   //--- Таблица списка символов
   x=2;
   //y=m_window_main.CaptionHeight()+m_menu_main.YSize()+1;
   y=m_menu_main.Y2()+1;
   w=140;

Компилируем, получаем:


Разве Y2() не должен вернуть уже рассчитанный нами ранее тот же Y2 ? m_window_main.CaptionHeight()+m_menu_main.YSize()+1;

Значит не должен - мы же теперь указываем не координаты со смещением от любого края окна, а только лишь смещение, и только лишь от верха или лева. Хорошо. Убираем из полученной координаты Y2() ещё и координату Y() верха окна-формы для получения величины смещения от верха окна-формы:

   //--- Таблица списка символов
   x=2;
   //y=m_window_main.CaptionHeight()+m_menu_main.YSize()+1;
   y=m_menu_main.Y2()-m_window_main.Y()+1;
   w=140;

Компилируем и получаем то, что хотели:


Выходит, что для двадцати объектов, идущих на расстоянии двух пикселей друг от друга необходимо всегда складывать их размеры плюс отступ друг от друга чтобы получить нужное смещение для последнего объекта? Ведь Y2() девятнадцатого объекта (от которого можно было ранее построить двадцатый объект лишь прибавив смещение в два пункта) теперь не даёт нам точки отсчёта, а возвращает не то, что ожидается... и приходится складывать размеры всех объектов для получения нужного смещения или для каждого объекта перед его созданием нужно высчитать его смещение исходя из его координаты Y2() и начальной координаты Y() окна-формы.

Вот никак не пойму. Анатолий, подскажи выход пожалуйста. Что я не так делаю? Почему вообще так? Почему ты считаешь, что теперь стало более дружественно? Если ранее можно было просто указывать смещение от любого края окна-формы, от любого объекта в этом окне, да хоть от края графика вообще для построения нужного объекта, то теперь нужно проделать танцы с бубном для высчитывания его координат перед его построением? Писать больше кода и разных расчётов разве лучше и удобнее?

Я понимаю, что ты автор, и тебе виднее. Но всё же ты даёшь пользоваться и другим. Я (моё мнение всего -лишь) думаю, что это нововведение оказалось единственным не лучшим решением для введения его в качестве обновления - стало хуже. Не стало тех возможностей лёгкого построения интерфейса, которые были ранее.

Извини если обидел (раньше гонцам с плохой вестью головы срубали)

 
Artyom Trishkin:

...

Извини если обидел (раньше гонцам с плохой вестью головы срубали)

Я отвечу чуть позже, но прежде, хотел бы уточнить, что ты хотел сказать этой фразой относительно меня. Что есть "плохая новость" и кто есть "гонец" в данном контексте? :)

 
Anatoli Kazharski:

Я отвечу чуть позже, но прежде, хотел бы уточнить, что ты хотел сказать этой фразой относительно меня. Что есть "плохая новость" и кто есть "гонец" в данном контексте? :)

:)

Плохой новостью я назвал своё отношение к нововведению что нельзя теперь за точку отсчёта координат объекта брать любую произвольную координату, а лишь левый и верхний края формы (у себя я добавил пересчёт координат в локальные - лишние расчёты)... Ну и гонец - я конечно, раз мне не понравилось, и я сказал об этом :)

 
Artyom Trishkin:

:)

Плохой новостью я назвал своё отношение к нововведению что нельзя теперь за точку отсчёта координат объекта брать любую произвольную координату, а лишь левый и верхний края формы (у себя я добавил пересчёт координат в локальные - лишние расчёты)... Ну и гонец - я конечно, раз мне не понравилось, и я сказал об этом :)

Вот оно что. Хорошо. А то мне показалось, что я не так понял. ) 

Вот мой ответ:

Artyom Trishkin:

Всё же, как мне кажется, последние изменения относительно указания координат по смещению от края формы не очень дружественны к конечному пользователю. Если раньше пользователь мог спокойно использовать два варианта указания места расположения объекта в окне, то теперь один из них усложнился. У меня так всё развалилось-разлетелось по всему экрану.

...

Теперь посмотрим что придётся проделать при построении интерфейса по новым правилам:

Кнопка1: x_gap1=4;
Кнопка2: x_gap2=4+m_butt1.XSize()+2;
Кнопка3: x_gap3=4+m_butt1.XSize()+2+m_butt2.XSize()+4;
РПолоса: x_gap4=4+m_butt1.XSize()+2+m_butt2.XSize()+4+m_butt3.XSize()+4;

...

Тут напрашивается вместо указания размеров объектов указывать их координату X2(), но не тут-то было - сразу же всё распадается (я, например, пока догадался, что Y2() не указывает на нужную точку расположения Y-координаты, всю голову себе сломал.)

Всё очень просто. Ранее, в методы создания элементов нужно было передать абсолютные координаты. Их нужно было рассчитывать относительно левой крайней точки формы, к которой присоединяется элемент. 

Пример того, что нужно было сделать раньше:

//+------------------------------------------------------------------+
//| Создаёт кнопку                                                   |
//+------------------------------------------------------------------+
bool CProgram::CreateSimpleButton(const int x_gap,const int y_gap,const string button_text)
  {
//--- Сохраним указатель на форму
   m_simple_button.WindowPointer(m_window);
//--- Координаты
   int x=m_window.X()+x_gap;
   int y=m_window.Y()+y_gap;

//--- Установим свойства перед созданием
   m_simple_button.ButtonXSize(100);
//--- Создание кнопки
   if(!m_simple_button.CreateSimpleButton(m_chart_id,m_subwin,button_text,x,y))
      return(false);
//--- Добавим указатель на элемент в базу
   CWndContainer::AddToElementsArray(0,m_simple_button);
   return(true);
  }

//---

А теперь ничего рассчитывать не нужно, так как достаточно передать относительные координаты.

Пример того, что нужно делать сейчас:

//+------------------------------------------------------------------+
//| Создаёт кнопку                                                   |
//+------------------------------------------------------------------+
bool CProgram::CreateSimpleButton(const int x_gap,const int y_gap,const string button_text)
  {
//--- Передать объект панели
   m_simple_button.WindowPointer(m_window);
//--- Установим свойства перед созданием
   m_simple_button.ButtonXSize(100);
//--- Создание кнопки
   if(!m_simple_button.CreateSimpleButton(m_chart_id,m_subwin,button_text,x_gap,y_gap))
      return(false);
//--- Добавим указатель на элемент в базу
   CWndContainer::AddToElementsArray(0,m_simple_button);
   return(true);
  }

//---

В твоих примерах выше ты рассчитываешь абсолютные координаты. Глупое решение с последующими неверными выводами поспешно вынесенными на публику. 

Методы CElement::X(), CElement::X2(), CElement::Y() и CElement::Y2() возвращают абсолютные координаты границ элементов. И это легко было понять просто выводя эти значения в журнал терминала, если есть какие-то сомнения. 

Ниже пример расчёта отступа (относительной координаты) относительно, какого-либо элемента. Есть две кнопки. Координата X (относительная) для второй кнопки рассчитывается относительно правой границы первой кнопки.

//+------------------------------------------------------------------+
//| Создаёт графический интерфейс программы                          |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- Создание панели
   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);
  }

//---

В итоге вторая кнопка будет установлена с отступом на 5 пикселей от правой границы первой кнопки:

//---

Изменяя ширину или X-координату первой кнопки положение второй кнопки будет автоматически подстраиваться.

Так что лишние расчёты только в твоём примере процитированном выше.

 
Anatoli Kazharski:

//+------------------------------------------------------------------+
//| Создаёт графический интерфейс программы                          |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- Создание панели
   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);
  }

//---

В итоге вторая кнопка будет установлена с отступом на 5 пикселей от правой границы первой кнопки:

//---

Изменяя ширину или X-координату первой кнопки положение второй кнопки будет автоматически подстраиваться.

Так что лишние расчёты только в твоём примере процитированном выше.

Вот... Просто стало не очень наглядно:

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

Приходится выносить этот пересчёт в свои функции чтобы не писать этот пересчёт везде при указании координат вновь создаваемого объекта, а продолжать писать так, как было раньше - нагляднее видно отношение одного объекта к другому. Дело вкуса и предпочтений каждого конечно...

Кстати, файловый навигатор рассыпается даже при указании координат как ты говоришь. Но может это только у меня. Пошёл искать причину.

 

Анатолий, похоже, ты забыл внести правки в FileNavigator.mqh в

//+------------------------------------------------------------------+
//| Создаёт древовидный список                                       |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\folder_w10.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\text_file_w10.bmp"
//---
bool CFileNavigator::CreateTreeView(void)
  {
//--- Сохраним указатель на окно
   m_treeview.WindowPointer(m_wnd);
//--- Установим свойства
   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);
//--- Формируем массивы древовидного списка
   int items_total=::ArraySize(m_g_item_text);
   for(int i=0; i<items_total; i++)
     {
      //--- Определим картинку для пункта (папка/файл)
      string icon_path=(m_g_is_folder[i])? m_folder_icon : m_file_icon;
      //--- Если это папка, удалим последний символ ('\') в строке
      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);
      //--- Добавим пункт в древовидный список
      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]);
     }
//--- Создать древовидный список
   if(!m_treeview.CreateTreeView(m_chart_id,m_subwin,m_x-1,m_y+m_address_bar_y_size-1))
      return(false);
//--- Сохраним размеры навигатора
   CElement::XSize(m_treeview.XSize());
   CElement::YSize(m_treeview.YSize()+m_address_bar_y_size);
   return(true);
  }
//+------------------------------------------------------------------+

Вот тут нужно добавить:

//--- Создать древовидный список
   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:

...

Кстати, файловый навигатор рассыпается даже при указании координат как ты говоришь. Но может это только у меня. Пошёл искать причину.

У себя не воспроизвёл:

 

 
Anatoli Kazharski:

У себя не воспроизвёл:

 

Проверь FileNavigator.mqh в последнем обновлении.
 
Artyom Trishkin:
Проверь FileNavigator.mqh в последнем обновлении.
Это и есть последняя.
 
Anatoli Kazharski:
Это и есть последняя.

Я имею в виду на сайте в статье - может там старый прибился к обновлениям. Я ж беру файл из последнего обновления, загруженного с сайта снизу статьи из зипа.

Вот, внёс правки как выше написал: