зміст
Вступ
У попередніх статтях серії про графічних інтерфейсах в середовищі торгових терміналів MetaTrader були розглянуті основні частини розробляється бібліотеки, а також створені кілька елементів інтерфейсу: головне меню, контекстне меню, рядок стану, кнопки, групи кнопок, спливаючі підказки. П'яту частину серії присвятимо таким елементам управління, як смуга прокрутки і список. У першому розділі напишемо класи для створення вертикальної і горизонтальної смуг прокрутки. У другому розділі розробимо складовий елемент інтерфейсу «Список». Складовим він є, тому що смуга прокрутки буде його частиною, тому почнемо саме з неї.
Елемент «Смуга прокрутки»
Смуги прокрутки використовуються в різних списках або таблицях, коли весь набір представлених даних не поміщається в позначену область. Основні об'єкти смуги прокрутки - кнопки для зміщення масиву даних на один крок і повзунок, за допомогою якого можна швидко зміщувати дані, перетягуючи їх, затиснувши лівою кнопкою миші.
Збирати смугу прокрутки ми будемо з п'яти графічних об'єктів.
- Основний фон.
- Фон області переміщення повзунка.
- Дві кнопки для зміщення масивів даних на один крок.
- Повзунок для швидкого переміщення масивів даних.
Мал. 1. Складові частини елемента управління «Смуга прокрутки».
Так як смуга прокрутки може бути двох типів (вертикальна і горизонтальна), то зручніше зробити для кожного типу окремий клас. Це будуть похідні класи для відображення унікальних особливостей кожного типу смуги прокрутки.
- CScrollV - похідний клас для вертикальної смуги прокрутки.
- CScrollH - похідний клас для горизонтальної смуги прокрутки.
Базовим класом для них стане (CScroll), в якому повинні міститися загальні для кожного типу поля і методи. Всі три класи помістимо в один файл Scrolls.mqh. Схема для елемента управління «Смуга прокрутки» тоді буде виглядати так:
Мал. 2. Схема елемента управління «Смуга прокрутки».
Далі розглянемо процес розробки базового класу цього елемента управління (CScroll).
Базовий клас елемента
Елемент управління «Смуга прокрутки» не є самостійним елементом графічного інтерфейсу. Це допоміжний елемент, і його потрібно буде підключати до інших елементів, для яких може знадобитися переміщення даних в робочій області. Тому файл Scrolls.mqh не потрібно підключати безпосередньо до файлу WndContainer.mqh. Класи смуги прокрутки будуть доступні в бібліотеці через підключення до файлів з класами інших елементів.
Створіть клас CScroll в файлі Scrolls.mqh зі стандартними для всіх елементів інтерфейсу методами:
#include "Element.mqh" #include "Window.mqh" class CScroll: public CElement {protected: CWindow * m_wnd; public: void WindowPointer (CWindow & object) {m_wnd = :: GetPointer (object); } Public: virtual void OnEvent (const int id, const long & lparam, const double & dparam, const string & sparam); virtual void OnEventTimer (void) {} virtual void Moving (const int x, const int y); virtual void Show (void); virtual void Hide (void); virtual void Reset (void); virtual void Delete (void); virtual void SetZorders (void); virtual void ResetZorders (void); virtual void ResetColors (void) {}}; CScroll :: CScroll (void) {} CScroll :: ~ CScroll (void) {}
Забігаючи трохи вперед, відзначимо, що в цьому ж файлі потрібно створити похідні класи, як це показано в лістингу коду нижче. Залежно від того, який тип класу буде використовуватися, елементу в конструкторі класу присвоюється відповідне ім'я класу.
class CScrollV: public CScroll {public: CScrollV (void); ~ CScrollV (void); }; CScrollV :: CScrollV (void) {CElement :: ClassName (CLASS_NAME); } CScrollV :: ~ CScrollV (void) {} class CScrollH: public CScroll {public: CScrollH (void); ~ CScrollH (void); }; CScrollH :: CScrollH (void) {CElement :: ClassName (CLASS_NAME); } CScrollH :: ~ CScrollH (void) {}
Як вже було сказано раніше, зробимо так, щоб була можливість зробити точну настройку зовнішнього вигляду смуги прокрутки. Для цього в базовому класі (CScroll) створимо поля і методи, за допомогою яких можна встановити наступні параметри об'єктів елемента:
- Ширина смуги прокрутки
- Колір рамки загального фону
- Колір фону і рамки області, в якій переміщається повзунок смуги прокрутки
- Колір фону і рамки повзунка в різних станах
- Картинки для кнопок в різних станах, за допомогою яких проводиться зсув даних
Слід уточнити, що шириною смуги прокрутки (а також інших її об'єктів) для вертикального типу буде розмір по осі X, а для горизонтального типу - розмір по осі Y. За замовчуванням ширина дорівнюватиме 15 пікселям. Картинки для кнопок за замовчуванням підібрані саме під цей розмір. Вони розташовані всередині загального фону елемента з відступом 1 піксель, щоб не затуляти рамку фону, і їх розмір дорівнює 13 x 13 пікселів. Тому якщо нам знадобиться змінити ширину смуги прокрутки, то потрібно буде перевизначити картинки для кнопок. Для цих цілей створимо відповідні методи.
Ширина для інших об'єктів смуги прокрутки (внутрішня область і повзунок) буде розраховуватися автоматично щодо ширини загального фону. Довжина теж буде розраховуватися автоматично, так як цей параметр залежить від кількості пунктів в списку і їх розміру по осі Y. Про те, як це буде реалізовано, читайте далі в статті.
class CScroll: public CElement {protected: int m_area_width; int m_area_length; color m_area_color; color m_area_border_color; int m_bg_length; color m_bg_border_color; string m_inc_file_on; string m_inc_file_off; string m_dec_file_on; string m_dec_file_off; color m_thumb_color; color m_thumb_color_hover; color m_thumb_color_pressed; color m_thumb_border_color; color m_thumb_border_color_hover; color m_thumb_border_color_pressed; int m_thumb_width; int m_thumb_length; int m_thumb_min_length; double m_thumb_step_size; double m_thumb_steps_total; int m_area_zorder; int m_bg_zorder; int m_arrow_zorder; int m_thumb_zorder; public: void ScrollWidth (const int width) {m_area_width = width; } Int ScrollWidth (void) const {return (m_area_width); } Void AreaColor (const color clr) {m_area_color = clr; } Void AreaBorderColor (const color clr) {m_area_border_color = clr; } Void BgBorderColor (const color clr) {m_bg_border_color = clr; } Void IncFileOn (const string file_path) {m_inc_file_on = file_path; } Void IncFileOff (const string file_path) {m_inc_file_off = file_path; } Void DecFileOn (const string file_path) {m_dec_file_on = file_path; } Void DecFileOff (const string file_path) {m_dec_file_off = file_path; } Void ThumbColor (const color clr) {m_thumb_border_color = clr; } Void ThumbColorHover (const color clr) {m_thumb_border_color_hover = clr; } Void ThumbColorPressed (const color clr) {m_thumb_border_color_pressed = clr; } Void ThumbBorderColor (const color clr) {m_thumb_border_color = clr; } Void ThumbBorderColorHover (const color clr) {m_thumb_border_color_hover = clr; } Void ThumbBorderColorPressed (const color clr) {m_thumb_border_color_pressed = clr; }}; CScroll :: CScroll (void): m_area_width (15), m_area_length (0), m_inc_file_on ( ""), m_inc_file_off ( ""), m_dec_file_on ( ""), m_dec_file_off ( ""), m_thumb_width (0), m_thumb_length (0 ), m_thumb_min_length (15), m_area_color (C'210,210,210 '), m_area_border_color (C'240,240,240'), m_bg_border_color (C'210,210,210 '), m_thumb_color (C'190,190,190'), m_thumb_color_hover (C'180,180,180 '), m_thumb_color_pressed (C '160,160,160'), m_thumb_border_color (C'170,170,170 '), m_thumb_border_color_hover (C'160,160,160'), m_thumb_border_color_pressed (C'140,140,140 ') {m_area_zorder = 8; m_bg_zorder = 9; m_arrow_zorder = 10; m_thumb_zorder = 11; }
Розглянемо методи створення елемента «Смуга прокрутки». Кожна його частина створюється окремим приватним методом. У головному методі в якості останніх двох параметрів потрібно передати розмір списку і розмір видимої частини списку. Ці параметри потім будуть брати участь в розрахунку кількості кроків для повзунка смуги прокрутки.
class CScroll: public CElement {protected: CRectLabel m_area; CRectLabel m_bg; CBmpLabel m_inc; CBmpLabel m_dec; CRectLabel m_thumb; public: bool CreateScroll (const long chart_id, const int subwin, const int x, const int y, const int items_total, const int visible_items_total); private: bool CreateArea (void); bool CreateBg (void); bool CreateInc (void); bool CreateDec (void); bool CreateThumb (void); };
Як приклад наведемо код тільки деяких з цих методів, у всіх інших використовуються подібні принципи, і вам буде нескладно розібратися з ними самостійно. Подивіться код методу CScroll :: CreateBg () в лістингу нижче. Зверніть увагу, що формування імені об'єкта залежить від типу смуги прокрутки, а також від того, чи не встановлено індекс для цього елемента. Вказівка індексу може знадобитися при розробці складних складових елементів управління, де може використовуватися кілька смуг прокрутки одного типу. Такі приклади будуть показані в одній з наступних статей.
Також від типу смуги прокрутки залежать розрахунки і визначення значень таких параметрів, як: (1) координати, (2) довжина області переміщення повзунка і (3) розміри. Ця частина виділена синім маркером в лістингу коду нижче.
bool CScroll :: CreateBg (void) {string name = ""; string name_part = (CElement :: ClassName () == "CScrollV")? "_Scrollv_bg_": "_scrollh_bg_"; if (CElement :: Index () == WRONG_VALUE) name = CElement :: ProgramName () + name_part + (string) CElement :: Id (); else name = CElement :: ProgramName () + name_part + (string) CElement :: Index () + "__" + (string) CElement :: Id (); int x = 0; int y = 0; int x_size = 0; int y_size = 0; if (CElement :: ClassName () == "CScrollV") {m_bg_length = CElement :: YSize () - (m_thumb_width * 2) - 2; x = CElement :: X () + 1; y = CElement :: Y () + m_thumb_width + 1; x_size = m_thumb_width; y_size = m_bg_length; } Else {m_bg_length = CElement :: XSize () - (m_thumb_width * 2) - 2; x = CElement :: X () + m_thumb_width + 1; y = CElement :: Y () + 1; x_size = m_bg_length; y_size = m_thumb_width; } If (! M_bg.Create (m_chart_id, name, m_subwin, x, y, x_size, y_size)) return (false); m_bg.BackColor (m_area_color); m_bg.Color (m_bg_border_color); m_bg.BorderType (BORDER_FLAT); m_bg.Corner (m_corner); m_bg.Selectable (false); m_bg.Z_Order (m_bg_zorder); m_bg.Tooltip ( "\ n"); m_bg.X (x); m_bg.Y (y); m_bg.XGap (x-m_wnd.X ()); m_bg.YGap (y-m_wnd.Y ()); m_bg.XSize (x_size); m_bg.YSize (y_size); CElement :: AddToArray (m_bg); return (true); }
При створенні графічного об'єкта, який буде виступати в якості повзунка смуги прокрутки, знадобиться метод для розрахунку його довжини. Цей параметр (довжина) залежить від кількості пунктів в списку і від кількості пунктів у видимій його частини. Створимо такий метод і назвемо його CScroll :: CalculateThumbSize ().
На самому початку цього методу варто перевірка на розмір області для переміщення повзунка. Якщо довжина цієї області менше мінімальної довжини повзунка, то розрахунок проводити не потрібно. Метод поверне false, і повзунок створений не буде, так як при таких умовах він просто не потрібен. Якщо ж перевірка пройдена, то далі розрахунок проводиться в кілька етапів:
- Розраховується розмір кроку повзунка.
- Якщо виявляється, що отримане значення менше 1, то коректуємо його, роблячи рівним 1.
- Розраховується розмір робочої області для переміщення повзунка.
- Якщо розмір робочої області вийшов менше, ніж розмір всієї області для переміщення повзунка, то робимо розрахунок довжини повзунка. В іншому випадку встановимо мінімальну довжину, за замовчуванням дорівнює 15 пікселям.
- В самому кінці проводиться перевірка отриманого розміру повзунка з урахуванням приведення до целочисленному типу ( int ) І його коригування в разі, якщо виявилося, що довжина менше мінімальної.
Детальніше з кодом методу CScroll :: CalculateThumbSize () можна ознайомитися в лістингу нижче:
class CScroll: public CElement {protected: public: bool CalculateThumbSize (void); }; bool CScroll :: CalculateThumbSize (void) {if (m_bg_length <m_thumb_min_length) return (false); m_thumb_step_size = (double) (m_bg_length-m_thumb_min_length) / m_thumb_steps_total; m_thumb_step_size = (m_thumb_step_size <1)? 1: m_thumb_step_size; double work_area = m_thumb_step_size * m_thumb_steps_total; double thumb_size = (work_area <m_bg_length)? m_bg_length-work_area + m_thumb_step_size: m_thumb_min_length; m_thumb_length = ((int) thumb_size <m_thumb_min_length)? m_thumb_min_length :( int) thumb_size; return (true); }
Викликати метод CScroll :: CalculateThumbSize () потрібно в методі CScroll :: CreateThumb () до створення об'єкта. Надалі буде показаний ще один випадок, коли довжину смуги прокрутки потрібно розраховувати в процесі використання елемента управління. Розглядати його будемо в процесі розробки елемента управління, де це буде необхідно.
bool CScroll :: CreateThumb (void) {string name = ""; string name_part = (CElement :: ClassName () == "CScrollV")? "_Scrollv_thumb_": "_scrollh_thumb_"; if (CElement :: Index () == WRONG_VALUE) name = CElement :: ProgramName () + name_part + (string) CElement :: Id (); else name = CElement :: ProgramName () + name_part + (string) CElement :: Index () + "__" + (string) CElement :: Id (); int x = 0; int y = 0; int x_size = 0; int y_size = 0; if (! CalculateThumbSize ()) return (true); if (CElement :: ClassName () == "CScrollV") {x = (m_thumb.X ()> 0)? m_thumb.X (): m_x + 1; y = (m_thumb.Y ()> 0)? m_thumb.Y (): m_y + m_thumb_width + 1; x_size = m_thumb_width; y_size = m_thumb_length; } Else {x = (m_thumb.X ()> 0)? m_thumb.X (): m_x + m_thumb_width + 1; y = (m_thumb.Y ()> 0)? m_thumb.Y (): m_y + 1; x_size = m_thumb_length; y_size = m_thumb_width; } If (! M_thumb.Create (m_chart_id, name, m_subwin, x, y, x_size, y_size)) return (false); m_thumb.BackColor (m_thumb_color); m_thumb.Color (m_thumb_border_color); m_thumb.BorderType (BORDER_FLAT); m_thumb.Corner (m_corner); m_thumb.Selectable (false); m_thumb.Z_Order (m_thumb_zorder); m_thumb.Tooltip ( "\ n"); m_thumb.X (x); m_thumb.Y (y); m_thumb.XGap (x-m_wnd.X ()); m_thumb.YGap (y-m_wnd.Y ()); m_thumb.XSize (x_size); m_thumb.YSize (y_size); CElement :: AddToArray (m_thumb); return (true); }
Інші методи створення об'єктів смуги прокрутки пропонуються для самостійного вивчення в доданому до статті файлі.
У головному (публічному) метод створення смуги прокрутки додамо перевірку на тип класу. У базовому класі елемента (CScroll) не зберігатися ім'я класу. Тому при спробі створити смугу прокрутки, використовуючи базовий клас, створення графічного інтерфейсу буде перервано, і в журнал роздрукується підказка про те, що потрібно використовувати похідні класи типу CScrollV або CScrollH. Зверніть також увагу на те, як не започатковано деякі параметри елемента. Такий підхід дозволяє максимально автоматизувати всі розрахунки, позбавивши користувачів бібліотеки від рутини, надаючи можливість вказати мінімальну кількість властивостей при створенні елементів управління з смугою прокрутки.
bool CScroll :: CreateScroll (const long chart_id, const int subwin, const int x, const int y, const int items_total, const int visible_items_total) {if (:: CheckPointer (m_wnd) == POINTER_INVALID) {:: Print (__FUNCTION__, "> Перед створенням скролла класу потрібно передати" "покажчик на форму: CScroll :: WindowPointer (CWindow & object)"); return (false); } If (CElement :: ClassName () == "") {:: Print (__FUNCTION__, "> Використовуйте похідні класи смуги прокрутки (CScrollV або CScrollH)."); return (false); } M_chart_id = chart_id; m_subwin = subwin; m_x = x; m_y = y; m_area_width = (CElement :: ClassName () == "CScrollV")? CElement :: XSize (): CElement :: YSize (); m_area_length = (CElement :: ClassName () == "CScrollV")? CElement :: YSize (): CElement :: XSize (); m_thumb_width = m_area_width- 2; m_thumb_steps_total = items_total-visible_items_total + 1; CElement :: XGap (m_x-m_wnd.X ()); CElement :: YGap (m_y-m_wnd.Y ()); if (! CreateArea ()) return (false); if (! CreateBg ()) return (false); if (! CreateInc ()) return (false); if (! CreateDec ()) return (false); if (! CreateThumb ()) return (false); if (m_wnd.WindowType () == W_DIALOG || m_wnd.IsMinimized ()) Hide (); return (true); }
Для визначення стану лівої кнопки миші щодо повзунка смуги прокрутки створимо перерахування ENUM_THUMB_MOUSE_STATE в файлі Enums.mqh.
enum ENUM_THUMB_MOUSE_STATE {THUMB_NOT_PRESSED = 0, THUMB_PRESSED_OUTSIDE = 1, THUMB_PRESSED_INSIDE = 2};
Крім цього, потрібно створити і відповідні поля і методи. Метод CScroll :: CheckMouseButtonState () потрібен для визначення того, над якою областю була затиснута ліва кнопка миші. Метод CScroll :: ZeroThumbVariables () використовується для обнулення змінних, пов'язаних з переміщенням повзунка і викликається в методі CScroll :: CheckMouseButtonState () за умовою, коли ліва кнопка миші відтиснуті.
Зверніть увагу також на те, що коли смуга прокрутки переводиться в режим переміщення повзунка, то форма блокується, і це при тому, що робиться це тільки тоді, коли елемент не є випадає. Потрібно це для того, щоб виключити режими підсвічування інших елементів при наведенні курсору в момент, коли повзунок смуги прокрутки знаходиться в режимі переміщення, а курсор вийшов за його межі. Коли режим переміщення повзунка відключений, то форму потрібно розблокувати в методі CScroll :: ZeroThumbVariables ().
class CScroll: public CElement {protected: bool m_scroll_state; int m_thumb_size_fixing; int m_thumb_point_fixing; ENUM_THUMB_MOUSE_STATE m_clamping_area_mouse; public: void CheckMouseButtonState (const bool mouse_state); void ZeroThumbVariables (void); }; void CScroll :: CheckMouseButtonState (const bool mouse_state) {if (! mouse_state) {ZeroThumbVariables (); return; } If (mouse_state) {if (m_clamping_area_mouse! = THUMB_NOT_PRESSED) return; if (! m_thumb.MouseFocus ()) m_clamping_area_mouse = THUMB_PRESSED_OUTSIDE; else {m_clamping_area_mouse = THUMB_PRESSED_INSIDE; if (! CElement :: IsDropdown ()) {m_wnd.IsLocked (true); m_wnd.IdActivatedElement (CElement :: Id ()); }}}} Void CScroll :: ZeroThumbVariables (void) {if (! CElement :: IsDropdown ()) {m_wnd.IsLocked (false); m_wnd.IdActivatedElement (WRONG_VALUE); } M_thumb_size_fixing = 0; m_clamping_area_mouse = THUMB_NOT_PRESSED; }
Для зміни кольору об'єктів смуги прокрутки в залежності від місця розташування курсора миші і стану її лівої кнопки, створимо метод CScroll :: ChangeObjectsColor (). На самому початку цього методу варто перевірка на те, заблокована форма і чи відрізняються ідентифікатори смуги прокрутки і ідентифікатора, який збережений в пам'яті форми. Якщо обидві ці умови виконуються, то далі, в залежності від поточного режиму смуги прокрутки і фокуса над її кнопками, для них встановлюється відповідний стан. У смуги прокрутки може бути два режими: (1) - вільна і (2) - в процесі переміщення повзунка. Після цього, в залежності від місця розташування курсора і того, в якій області була затиснута ліва кнопка миші, для повзунка смуги прокрутки встановлюється відповідний колір. Тут же визначається і режим смуги прокрутки.
class CScroll: public CElement {public: void ChangeObjectsColor (void); }; void CScroll :: ChangeObjectsColor (void) {if (m_wnd.IsLocked () && m_wnd.IdActivatedElement ()! = CElement :: Id ()) return; if (! m_scroll_state) {m_inc.State (m_inc.MouseFocus ()); m_dec.State (m_dec.MouseFocus ()); } If (m_thumb.MouseFocus ()) {if (m_clamping_area_mouse == THUMB_NOT_PRESSED) {m_scroll_state = false; m_thumb.BackColor (m_thumb_color_hover); m_thumb.Color (m_thumb_border_color_hover); } Else if (m_clamping_area_mouse == THUMB_PRESSED_INSIDE) {m_scroll_state = true; m_thumb.BackColor (m_thumb_color_pressed); m_thumb.Color (m_thumb_border_color_pressed); }} Else {if (m_clamping_area_mouse == THUMB_NOT_PRESSED) {m_scroll_state = false; m_thumb.BackColor (m_thumb_color); m_thumb.Color (m_thumb_border_color); }}}
При розробці елементів інтерфейсу, в яких буде використовуватися смуга прокрутки, знадобляться методи для отримання імен і станів її кнопок. Крім цього, потрібні методи для установки і визначення стану переміщення повзунка смуги прокрутки і його поточної позиції щодо списку, до якого він приєднаний.
class CScroll: public CElement {protected: bool m_scroll_state; int m_current_pos; public: string ScrollIncName (void) const {return (m_inc.Name ()); } String ScrollDecName (void) const {return (m_dec.Name ()); } Bool ScrollIncState (void) const {return (m_inc.State ()); } Bool ScrollDecState (void) const {return (m_dec.State ()); } Void ScrollState (const bool scroll_state) {m_scroll_state = scroll_state; } Bool ScrollState (void) const {return (m_scroll_state); } Void CurrentPos (const int pos) {m_current_pos = pos; } Int CurrentPos (void) const {return (m_current_pos); }};
Розробка базового класу смуги прокрутки завершена. Далі ми займемося наповненням похідних класів цього елемента управління.
Похідні класи елемента
Якщо базовий клас смуги прокрутки наповнений загальними методами для створення об'єктів, налаштування параметрів і отримання їх значень, то похідні класи призначені для управління цим елементом. Раніше ми вже створили похідні класи CScrollV і CScrollH в файлі Scrolls.mqh. Розглянемо методи тільки одного з них - вертикального типу (CScrollV). У другому (CScrollH) буде все те ж саме, але з поправкою на те, що це горизонтальний тип, а саме: якщо в класі вертикальної смуги прокрутки ведеться робота з координатою Y, то в класі горизонтального типу цього елемента ведеться робота з координатою X.
Спочатку розглянемо код методів CScrollV :: OnDragThumb () і CScrollV :: UpdateThumb (), призначених для переміщення повзунка смуги прокрутки. Оголосити їх потрібно в приватній секції класу, оскільки використовуватися вони будуть тільки всередині класу CScrollV.
class CScrollV: public CScroll {private: void OnDragThumb (const int y); void UpdateThumb (const int new_y_point); };
Метод CScrollV :: OnDragThumb () потрібен для визначення того, що відбувається спроба переміщення повзунка користувачем. Якщо ліва кнопка миші була затиснута над повзунком смуги прокрутки і в такому стані відбулося зміщення курсору по осі Y, то, значить, процес переміщення почався, і потрібно змістити об'єкт. Оновлення координат повзунка проводиться за допомогою методу CScrollV :: UpdateThumb ().
void CScrollV :: OnDragThumb (const int y) {int new_y_point = 0; if (! CScroll :: ScrollState ()) {CScroll :: m_thumb_size_fixing = 0; CScroll :: m_thumb_point_fixing = 0; return; } If (CScroll :: m_thumb_point_fixing == 0) CScroll :: m_thumb_point_fixing = y; if (CScroll :: m_thumb_size_fixing == 0) CScroll :: m_thumb_size_fixing = m_thumb.Y () - y; if (y-CScroll :: m_thumb_point_fixing> 0) {new_y_point = y + CScroll :: m_thumb_size_fixing; UpdateThumb (new_y_point); return; } If (y-CScroll :: m_thumb_point_fixing <0) {new_y_point = y- :: fabs (CScroll :: m_thumb_size_fixing); UpdateThumb (new_y_point); return; }}
У лістингу нижче можна докладніше вивчити код методу CScrollV :: UpdateThumb (). У нього передається розрахована в методі CScrollV :: OnDragThumb () координата Y. Тут вона перевіряється на коректність. Якщо виявиться, що ми виходимо за межі робочої області, призначеної для переміщення повзунка, то значення коригуються. Тільки після цього здійснюється оновлення координат і їх збереження в полях базових класів.
void CScrollV :: UpdateThumb (const int new_y_point) {int y = new_y_point; CScroll :: m_thumb_point_fixing = 0; if (new_y_point> m_bg.Y2 () - CScroll :: m_thumb_length) {y = m_bg.Y2 () - CScroll :: m_thumb_length; CScroll :: CurrentPos (int (CScroll :: m_thumb_steps_total)); } If (new_y_point <= m_bg.Y ()) {y = m_bg.Y (); CScroll :: CurrentPos (0); } M_thumb.Y (y); m_thumb.Y_Distance (y); m_thumb.YGap (m_thumb.Y () - (CElement :: Y () - CElement :: YGap ())); }
Нам знадобиться ще один приватний метод - для коригування поточної позиції повзунка щодо його координати Y. Назвемо його CScrollV :: CalculateThumbPos (), з його кодом можна ознайомитися в лістингу нижче.
class CScrollV: public CScroll {private: void CalculateThumbPos (void); }; void CScrollV :: CalculateThumbPos (void) {if (CScroll :: m_thumb_step_size == 0) return; CScroll :: CurrentPos (int ((m_thumb.Y () - m_bg.Y ()) / CScroll :: m_thumb_step_size)); if (m_thumb.Y2 ()> = m_bg.Y2 () - 1) CScroll :: CurrentPos (int (CScroll :: m_thumb_steps_total- 1)); if (m_thumb.Y () <m_bg.Y ()) CScroll :: CurrentPos (0); }
Тепер створимо публічний метод CScrollV :: ScrollBarControl () для управління повзунком, в якому будуть викликатися все приватні методи, представлені вище. Його потрібно буде викликати в обробниках подій OnEvent () тих елементів, де буде використовуватися смуга прокрутки. До докладного наприклад, як це буде працювати, повернемося у другому розділі, в якій буде розглядатися клас для створення елемента управління «Список».
Нижче представлений код методу CScrollV :: ScrollBarControl (). В якості аргументів йому передаються координати курсора і стан лівої кнопки миші. На самому початку перевіряється фокус над повзунком смуги прокрутки. Потім визначається, над якою областю було вироблено затискання лівої кнопки миші. Залежно від місця розташування курсора і стану лівої кнопки, змінюється колір повзунка. Після цього, якщо управління передано смузі прокрутки, повзунок зміщується за новою розрахованою координаті Y, і вже щодо неї розраховується номер позиції в списку.
class CScrollV: public CScroll {public: bool ScrollBarControl (const int x, const int y, const bool mouse_state); }; bool CScrollV :: ScrollBarControl (const int x, const int y, const bool mouse_state) {m_thumb.MouseFocus (x> m_thumb.X () && x <m_thumb.X2 () && y> m_thumb.Y () && y <m_thumb .Y2 ()); CScroll :: CheckMouseButtonState (mouse_state); CScroll :: ChangeObjectsColor (); if (CScroll :: ScrollState ()) {OnDragThumb (y); CalculateThumbPos (); return (true); } Return (false); }
Якщо метод CScrollV :: CalculateThumbPos () перетворює координату Y в номер позиції в списку, то, крім інших, нам знадобиться метод, який робить зворотне перетворення, тобто, для розрахунку координати Y щодо поточної позиції повзунка. Тут також стоять перевірки на вихід з робочої області і коригування в разі виходу. В самому кінці методу координати і відступи об'єкта оновлюються.
class CScrollV: public CScroll {public: void CalculateThumbY (void); }; void CScrollV :: CalculateThumbY (void) {int scroll_thumb_y = int (m_bg.Y () + (CScroll :: CurrentPos () * CScroll :: m_thumb_step_size)); if (scroll_thumb_y <= m_bg.Y ()) scroll_thumb_y = m_bg.Y (); if (scroll_thumb_y + CScroll :: m_thumb_length> = m_bg.Y2 () || CScroll :: CurrentPos ()> = CScroll :: m_thumb_steps_total- 1) {scroll_thumb_y = int (m_bg.Y2 () - CScroll :: m_thumb_length); } M_thumb.Y (scroll_thumb_y); m_thumb.Y_Distance (scroll_thumb_y); m_thumb.YGap (m_thumb.Y () - m_wnd.Y ()); }
Викликатися метод CScrollV :: CalculateThumbY () буде в методах обробки натискання на кнопках смуги прокрутки (див. Лістинг коду нижче). В методи CScrollV :: OnClickScrollInc () і CScrollV :: OnClickScrollDec () в якості єдиного аргументу передається ім'я об'єкта. На самому початку перевіряються наступні параметри.
- Чи було натискання саме на цій кнопці. Перевірка на ім'я об'єкта.
- Стан смуги прокрутки. Щоб пройти перевірку, повинен бути режим «вільно».
- Кількість кроків має бути визначено, і це значення не може бути менше одиниці.
Якщо всі перевірки пройдені, то далі проводиться зсув позиції на один крок з урахуванням недопущення виходу з робочого діапазону. Після цього за допомогою методу CScrollV :: CalculateThumbY () розраховується і оновлюється в об'єкті координата Y і відступи від крайньої точки форми. Кнопка після натискання повинна залишатися в стані «включено».
class CScrollV: public CScroll {public: bool OnClickScrollInc (const string clicked_object); bool OnClickScrollDec (const string clicked_object); }; bool CScrollV :: OnClickScrollInc (const string clicked_object) {if (m_inc.Name ()! = clicked_object || CScroll :: ScrollState () || CScroll :: m_thumb_steps_total <1) return (false); if (CScroll :: CurrentPos ()> 0) CScroll :: m_current_pos--; CalculateThumbY (); m_inc.State (true); return (true); } Bool CScrollV :: OnClickScrollDec (const string clicked_object) {if (m_dec.Name ()! = Clicked_object || CScroll :: ScrollState () || CScroll :: m_thumb_steps_total <1) return (false); if (CScroll :: CurrentPos () <CScroll :: m_thumb_steps_total- 1) CScroll :: m_current_pos ++; CalculateThumbY (); m_dec.State (true); return (true); }
На цьому розробка всіх методів, необхідних для управління вертикальною смугою прокрутки, закінчена. Для горизонтального типу методи аналогічні, відмінність полягає тільки в тому, що розрахунки проводяться з координатою X.
Висновок
У цій статті (першому розділі п'ятої частини) ми розглянули такі елементи управління, як вертикальна і горизонтальна смуга прокрутки. У другому розділі п'ятої частини займемося розробкою елементу графічного інтерфейсу «Список». Кількість пунктів списку може не поміщатися в виділений простір, і саме для таких випадків нам знадобиться елемент управління «Смуга прокрутки», який ми і розробили в цій статті.
Нижче ви можете завантажити до себе на комп'ютер весь матеріал п'ятої частини серії, щоб у Вас відразу була можливість протестувати, як все це працює. Якщо у Вас виникають питання щодо використання матеріалу, наданого в цих файлах, то Ви можете звернутися до докладного опису процесу розробки бібліотеки в одній зі статей в представленому списку нижче або задати питання в коментарях до статті.
Список статей (глав) п'ятої частини:
Bool CScroll :: CreateBg (void) {string name = ""; string name_part = (CElement :: ClassName () == "CScrollV")?M_thumb_step_size; double work_area = m_thumb_step_size * m_thumb_steps_total; double thumb_size = (work_area <m_bg_length)?
M_bg_length-work_area + m_thumb_step_size: m_thumb_min_length; m_thumb_length = ((int) thumb_size <m_thumb_min_length)?
Bool CScroll :: CreateThumb (void) {string name = ""; string name_part = (CElement :: ClassName () == "CScrollV")?
X (): m_x + 1; y = (m_thumb.Y ()> 0)?
Y (): m_y + m_thumb_width + 1; x_size = m_thumb_width; y_size = m_thumb_length; } Else {x = (m_thumb.X ()> 0)?
X (): m_x + m_thumb_width + 1; y = (m_thumb.Y ()> 0)?
Return (false); } M_chart_id = chart_id; m_subwin = subwin; m_x = x; m_y = y; m_area_width = (CElement :: ClassName () == "CScrollV")?
CElement :: XSize (): CElement :: YSize (); m_area_length = (CElement :: ClassName () == "CScrollV")?