Графические интерфейсы IX: Элемент "Палитра для выбора цвета" (Глава 1)
Содержание
Введение
Более подробно о том, для чего предназначена эта библиотека, можно прочитать в самой первой статье: Графические интерфейсы I: Подготовка структуры библиотеки (Глава 1). В конце статей каждой части представлен список глав со ссылками, там же есть возможность загрузить к себе на компьютер полную версию библиотеки на текущей стадии разработки. Файлы нужно разместить по тем же директориям, как они расположены в архиве.
Девятая часть серии расскажет о следующих элементах управления и интерфейса:
1. Первая глава:
- Элемент «Палитра для выбора цвета» (класс CColorPicker).
- Элемент «Кнопка для вызова цветовой палитры» (класс CColorButton).
2. Вторая глава:
- Элемент «Индикатор выполнения» (класс CProgressBar).
- Элемент «Линейный график» (класс CLineGraph).
Для каждого из этих элементов будут показаны исчерпывающие примеры того, как их можно использовать в своих приложениях.
Элемент «Палитра для выбора цвета»
Цветовую палитру можно увидеть во множестве различных приложений, в которых предоставляется возможность указать цвет какого-нибудь объекта. В торговых терминалах MetaTrader цветовая палитра может понадобиться для быстрого изменения цвета элементов в вашем MQL-приложении. Допустим, при создании визуальной студии для создания графических интерфейсов, когда нужно настроить цветовое решение для каждого элемента, без цветовой палитры это делать было бы довольно неудобно.
Цветовая палитра — это сложный составной элемент управления, в котором, кроме самой палитры, отображающей выбранную цветовую модель, есть и другие объекты и группы элементов. Перечислим все составные части этого элемента.
- Фон
- Цветовая палитра, отображающая указанную цветовую модель
- Маркер установленного цвета
- Маркер выбранного цвета
- Маркер цвета по наведению курсора мыши
- Группа радио-кнопок с полями ввода для ручной настройки компонентов цветовой модели
- Кнопка для отмены выбранного цвета
- Кнопка для установки (фиксации) цвета, указанного во втором маркере
Рис. 1. Составные части элемента «Цветовая палитра для выбора цвета».
На скриншоте выше видно, что группа радио-кнопок разделена на три подгруппы – по три радио-кнопки в каждой. Каждая из этих подгрупп представляет собой цветовую модель, а каждая радио-кнопка – компонент этой модели (координату цветового пространства). В списке ниже приводится расшифровка аббревиатур всех цветовых моделей, которые будут использоваться при создании цветовой палитры для разрабатываемой библиотеки.
1. Цветовая модель HSL:
- H (Hue) – цветовой тон. Диапазон значений от 0 до 360.
- S (Saturation) – насыщенность. Диапазон значений от 0 до 100.
- L (Lightness) – светлота. Диапазон значений от 0 до 100.
2. Цветовая модель RGB:
- R (Red) – красный цвет. Диапазон значений от 0 до 255.
- G (Green) – зелёный цвет. Диапазон значений от 0 до 255.
- B (Blue) – синий цвет. Диапазон значений от 0 до 255.
3. Цветовая модель Lab:
- L (Luminance) – яркость. Диапазон значений от 0 до 100.
- a – первая хроматическая координата, определяющая оттенок цвета от зелёного до пурпурного. Диапазон значений от -128 до 127.
- b – вторая хроматическая координата, определяющая оттенок цвета от синего до жёлтого. Диапазон значений от -128 до 127.
Далее рассмотрим, как устроен класс CColorPicker для создания цветовой палитры.
Разработка класса CColorPicker
Создаём файл ColorPicker.mqh в той же директории, где расположены сейчас файлы всех остальных элементов управления (<каталог данных>\MQLX\Include\EasyAndFastGUI\Controls). В этом файле нужно создать класс CColorPicker со стандартными членами, как показано в листинге кода ниже:
//+------------------------------------------------------------------+ //| ColorPicker.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" //+------------------------------------------------------------------+ //| Класс для создания цветовой палитры для выбора цвета | //+------------------------------------------------------------------+ class CColorPicker : public CElement { private: //--- Указатель на форму, к которой элемент присоединён CWindow *m_wnd; //--- public: CColorPicker(void); ~CColorPicker(void); //--- 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); //--- (1) Показ, (2) скрытие, (3) сброс, (4) удаление virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- (1) Установка, (2) сброс приоритетов на нажатие левой кнопки мыши virtual void SetZorders(void); virtual void ResetZorders(void); //--- Сбросить цвет virtual void ResetColors(void) {} };
Для настройки графических объектов элемента будут доступны следующие свойства:
- Цвет фона элемента
- Цвет рамки фона элемента
- Цвет рамки цветовой палитры и маркеров цвета
Свойства графических объектов других элементов управления можно будет изменить, получив их указатель.
class CColorPicker : public CElement { private: //--- Цвет (1) фона и (2) рамки фона color m_area_color; color m_area_border_color; //--- Цвет рамки палитры color m_palette_border_color; //--- public: //--- Установка цвета (1) фона и (2) рамки фона, (3) рамки палитры void AreaBackColor(const color clr) { m_area_color=clr; } void AreaBorderColor(const color clr) { m_area_border_color=clr; } void PaletteBorderColor(const color clr) { m_palette_border_color=clr; } };
Так как в качестве составных частей цветовой палитры будут использоваться и другие элементы, то к файлу ColorPicker.mqh нужно подключить файлы с классами этих элементов. Для создания цветовой палитры понадобятся семнадцать приватных (private) методов и один публичный (public).
//+------------------------------------------------------------------+ //| ColorPicker.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "SpinEdit.mqh" #include "SimpleButton.mqh" #include "RadioButtons.mqh" //+------------------------------------------------------------------+ //| Класс для создания цветовой палитры | //+------------------------------------------------------------------+ class CColorPicker : public CElement { private: //--- Объекты для создания элемента CRectLabel m_area; CRectCanvas m_canvas; CRectLabel m_current; CRectLabel m_picked; CRectLabel m_hover; //--- CRadioButtons m_radio_buttons; CSpinEdit m_hsl_h_edit; CSpinEdit m_hsl_s_edit; CSpinEdit m_hsl_l_edit; //--- CSpinEdit m_rgb_r_edit; CSpinEdit m_rgb_g_edit; CSpinEdit m_rgb_b_edit; //--- CSpinEdit m_lab_l_edit; CSpinEdit m_lab_a_edit; CSpinEdit m_lab_b_edit; //--- CSimpleButton m_button_ok; CSimpleButton m_button_cancel; //--- public: //--- Методы для создания элемента bool CreateColorPicker(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateArea(void); bool CreatePalette(void); bool CreateCurrentSample(void); bool CreatePickedSample(void); bool CreateHoverSample(void); bool CreateRadioButtons(void); bool CreateHslHEdit(void); bool CreateHslSEdit(void); bool CreateHslLEdit(void); bool CreateRgbREdit(void); bool CreateRgbGEdit(void); bool CreateRgbBEdit(void); bool CreateLabLEdit(void); bool CreateLabAEdit(void); bool CreateLabBEdit(void); bool CreateButtonOK(const string text); bool CreateButtonCancel(const string text); };
При переключении радио-кнопки рядом с цветовой палитрой будут отображаться двумерные срезы цветовых пространств, в соответствии с указанным значением выделенного компонента. Другими словами, для отрисовки каждого среза нужно сначала осуществить расчёт относительно текущего значения его компоненты. Поэтому для каждой цветовой модели мы напишем три отдельных метода. Во все эти методы будет передаваться индекс выделенной радио-кнопки (индекс выделенного компонента).
Для преобразования цвета мы используем методы экземпляра класса CColors, который объявлен в базовом классе элемента (CElement). Для конвертации из формата RGB в формат Lab в классе CColors нет подходящего метода. Поэтому, там где нужна конвертация RGB->Lab, будет использоваться двойное преобразование через цветовую мастер-модель XYZ, то есть: RGB->XYZ->Lab. Для расчётов и хранения значений компонент всех цветовых моделей в классе CColorPicker нужно объявить соответствующие поля.
class CColorPicker : public CElement { private: //--- Значения компонентов в разных цветовых моделях: // HSL double m_hsl_h; double m_hsl_s; double m_hsl_l; //--- RGB double m_rgb_r; double m_rgb_g; double m_rgb_b; //--- Lab double m_lab_l; double m_lab_a; double m_lab_b; //--- XYZ double m_xyz_x; double m_xyz_y; double m_xyz_z; //--- private: //--- Рисует палитру по цветовой модели HSL (0: H, 1: S, 2: L) void DrawHSL(const int index); //--- Рисует палитру по цветовой модели RGB (3: R, 4: G, 5: B) void DrawRGB(const int index); //--- Рисует палитру по цветовой модели LAB (6: L, 7: a, 8: b) void DrawLab(const int index); };
В качестве примера приведём здесь код только одного из этих методов - CColorPicker::DrawHSL(), так как отличие между ними состоит только в предварительном расчёте значения компонент перед преобразованием. С кодом остальных методов вы можете ознакомиться в приложенных к статье файлах.
Расчёт и отрисовка осуществляется для каждого пикселя изображения. Обратите также внимание, что расчёты производятся относительно размеров цветовой палитры. То есть, используя этот код, можно создать подобный элемент управления, в котором цветовая палитра будет других размеров и необязательно в форме квадрата.
Рис. 2. Пример цветовой палитры с размером 500x255 пикселей.
//+------------------------------------------------------------------+ //| Рисует палитру HSL | //+------------------------------------------------------------------+ void CColorPicker::DrawHSL(const int index) { switch(index) { //--- Hue (H) - цветовой тон в диапазоне от 0 до 360 case 0 : { //--- Рассчитаем H-компоненту m_hsl_h=m_hsl_h_edit.GetValue()/360.0; //--- for(int ly=0; ly<m_canvas.YSize(); ly++) { //--- Рассчитаем L-компоненту m_hsl_l=ly/(double)m_canvas.YSize(); //--- for(int lx=0; lx<m_canvas.XSize(); lx++) { //--- Рассчитаем S-компоненту m_hsl_s=lx/(double)m_canvas.XSize(); //--- Конвертация HSL-компонент в RGB-компоненты m_clr.HSLtoRGB(m_hsl_h,m_hsl_s,m_hsl_l,m_rgb_r,m_rgb_g,m_rgb_b); //--- Соединим каналы uint rgb_color=XRGB(m_rgb_r,m_rgb_g,m_rgb_b); m_canvas.PixelSet(lx,m_canvas.YSize()-ly,rgb_color); } } break; } //--- Saturation (S) - насыщенность в диапазоне от 0 до 100 case 1 : { //--- Рассчитаем S-компоненту m_hsl_s=m_hsl_s_edit.GetValue()/100.0; //--- for(int ly=0; ly<m_canvas.YSize(); ly++) { //--- Рассчитаем L-компоненту m_hsl_l=ly/(double)m_canvas.YSize(); //--- for(int lx=0; lx<m_canvas.XSize(); lx++) { //--- Рассчитаем H-компоненту m_hsl_h=lx/(double)m_canvas.XSize(); //--- Конвертация HSL-компонент в RGB-компоненты m_clr.HSLtoRGB(m_hsl_h,m_hsl_s,m_hsl_l,m_rgb_r,m_rgb_g,m_rgb_b); //--- Соединим каналы uint rgb_color=XRGB(m_rgb_r,m_rgb_g,m_rgb_b); m_canvas.PixelSet(lx,m_canvas.YSize()-ly,rgb_color); } } break; } //--- Lightness (L) - яркость в диапазоне от 0 до 100 case 2 : { //--- Рассчитаем L-компоненту m_hsl_l=m_hsl_l_edit.GetValue()/100.0; //--- for(int ly=0; ly<m_canvas.YSize(); ly++) { //--- Рассчитаем S-компоненту m_hsl_s=ly/(double)m_canvas.YSize(); //--- for(int lx=0; lx<m_canvas.XSize(); lx++) { //--- Рассчитаем H-компоненту m_hsl_h=lx/(double)m_canvas.XSize(); //--- Конвертация HSL-компонент в RGB-компоненты m_clr.HSLtoRGB(m_hsl_h,m_hsl_s,m_hsl_l,m_rgb_r,m_rgb_g,m_rgb_b); //--- Соединим каналы uint rgb_color=XRGB(m_rgb_r,m_rgb_g,m_rgb_b); m_canvas.PixelSet(lx,m_canvas.YSize()-ly,rgb_color); } } break; } } }
Для рисования рамки на полотне цветовой палитры напишем метод CColorPicker::DrawPaletteBorder():
class CColorPicker : public CElement { private: //--- Рисует рамку палитры void DrawPaletteBorder(void); }; //+------------------------------------------------------------------+ //| Рисует рамку палитры | //+------------------------------------------------------------------+ void CColorPicker::DrawPaletteBorder(void) { //--- Размер палитры int x_size=m_canvas.XSize()-1; int y_size=m_canvas.YSize()-1; //--- Нарисовать рамку m_canvas.Line(0,0,x_size,0,m_palette_border_color); m_canvas.Line(0,y_size,x_size,y_size,m_palette_border_color); m_canvas.Line(0,0,0,y_size,m_palette_border_color); m_canvas.Line(x_size,0,x_size,y_size,m_palette_border_color); }
Все перечисленные методы для рисования в итоге будут вызываться в главном методе для рисования цветовой палитры CColorPicker::DrawPalette():
class CColorPicker : public CElement { private: //--- Рисует палитру void DrawPalette(const int index); }; //+------------------------------------------------------------------+ //| Рисует палитру | //+------------------------------------------------------------------+ void CColorPicker::DrawPalette(const int index) { switch(index) { //--- HSL (0: H, 1: S, 2: L) case 0 : case 1 : case 2 : { DrawHSL(index); break; } //--- RGB (3: R, 4: G, 5: B) case 3 : case 4 : case 5 : { DrawRGB(index); break; } //--- LAB (6: L, 7: a, 8: b) case 6 : case 7 : case 8 : { DrawLab(index); break; } } //--- Нарисуем рамку палитры DrawPaletteBorder(); //--- Обновим палитру m_canvas.Update(); }
При выборе цвета на палитре или во время настройки той или иной компоненты любой из представленных в элементе цветовых моделей, автоматически будут пересчитываться значения во всех полях ввода. Нужны методы, с помощью которых будут рассчитываться компоненты всех цветовых моделей элемента относительно той, чей срез (выделенная радио-кнопка) отображается на палитре в текущий момент.
Прежде всего, для работы понадобятся методы корректировки компонент RGB- и HSL-моделей, которые будут вызываться во многих других методах класса:
class CColorPicker : public CElement { private: //--- Корректировка компонент RGB void AdjustmentComponentRGB(void); //--- Корректировка компонент HSL void AdjustmentComponentHSL(void); }; //+------------------------------------------------------------------+ //| Корректировка компонент RGB | //+------------------------------------------------------------------+ void CColorPicker::AdjustmentComponentRGB(void) { m_rgb_r=::fmin(::fmax(m_rgb_r,0),255); m_rgb_g=::fmin(::fmax(m_rgb_g,0),255); m_rgb_b=::fmin(::fmax(m_rgb_b,0),255); } //+------------------------------------------------------------------+ //| Корректировка компонент HSL | //+------------------------------------------------------------------+ void CColorPicker::AdjustmentComponentHSL(void) { m_hsl_h*=360; m_hsl_s*=100; m_hsl_l*=100; }
После расчёта всех компонент нужно установить новые значения в поля вода элемента. В некоторых случаях может понадобится (1) установить значения всех компонент, а иногда (2) для всех, кроме выделенной в текущий момент. Для таких ситуаций напишем метод CColorPicker::SetControls(), который может работать в двух режимах.
class CColorPicker : public CElement { private: //--- Установка текущих параметров в поля ввода void SetControls(const int index,const bool fix_selected); }; //+------------------------------------------------------------------+ //| Установка текущих параметров в поля ввода | //+------------------------------------------------------------------+ void CColorPicker::SetControls(const int index,const bool fix_selected) { //--- Если нужно зафиксировать значение в поле ввода выделенной радио-кнопки if(fix_selected) { //--- Компоненты HSL if(index!=0) m_hsl_h_edit.ChangeValue(m_hsl_h); if(index!=1) m_hsl_s_edit.ChangeValue(m_hsl_s); if(index!=2) m_hsl_l_edit.ChangeValue(m_hsl_l); //--- Компоненты RGB if(index!=3) m_rgb_r_edit.ChangeValue(m_rgb_r); if(index!=4) m_rgb_g_edit.ChangeValue(m_rgb_g); if(index!=5) m_rgb_b_edit.ChangeValue(m_rgb_b); //--- Компоненты Lab if(index!=6) m_lab_l_edit.ChangeValue(m_lab_l); if(index!=7) m_lab_a_edit.ChangeValue(m_lab_a); if(index!=8) m_lab_b_edit.ChangeValue(m_lab_b); return; } //--- Если нужно скорректировать значения в полях ввода всех цветовых моделей m_hsl_h_edit.ChangeValue(m_hsl_h); m_hsl_s_edit.ChangeValue(m_hsl_s); m_hsl_l_edit.ChangeValue(m_hsl_l); //--- m_rgb_r_edit.ChangeValue(m_rgb_r); m_rgb_g_edit.ChangeValue(m_rgb_g); m_rgb_b_edit.ChangeValue(m_rgb_b); //--- m_lab_l_edit.ChangeValue(m_lab_l); m_lab_a_edit.ChangeValue(m_lab_a); m_lab_b_edit.ChangeValue(m_lab_b); }
Для расчёта компонент всех цветовых моделей элемента, относительно той, чей срез (выделенная радио-кнопка) отображается на палитре в текущий момент, напишем три отдельных метода: CColorPicker::SetHSL(), CColorPicker::SetRGB() и CColorPicker::SetLab(). Так как эти методы очень похожи по своему содержанию, приведём здесь код только одного из них — CColorPicker::SetRGB(). В самом начале этого метода получаем в поля класса значения из полей ввода RGB-модели. Полученные значения преобразовываем в HSL- и Lab-формат. После этого в самом конце вызываем метод CColorPicker::SetControls() в режиме установки значений для всех цветовых моделей элемента (false).
class CColorPicker : public CElement { private: //--- Установка параметров цветовых моделей относительно (1) HSL, (2) RGB, (3) Lab void SetHSL(void); void SetRGB(void); void SetLab(void); }; //+------------------------------------------------------------------+ //| Установка параметров цветовых моделей относительно RGB | //+------------------------------------------------------------------+ void CColorPicker::SetRGB(void) { //--- Получим текущие значения компонентов RGB m_rgb_r=m_rgb_r_edit.GetValue(); m_rgb_g=m_rgb_g_edit.GetValue(); m_rgb_b=m_rgb_b_edit.GetValue(); //--- Конвертация RGB-компонент в HSL-компоненты m_clr.RGBtoHSL(m_rgb_r,m_rgb_g,m_rgb_b,m_hsl_h,m_hsl_s,m_hsl_l); //--- Корректировка компонент HSL AdjustmentComponentHSL(); //--- Конвертация RGB-компонент в Lab-компоненты m_clr.RGBtoXYZ(m_rgb_r,m_rgb_g,m_rgb_b,m_xyz_x,m_xyz_y,m_xyz_z); m_clr.XYZtoCIELab(m_xyz_x,m_xyz_y,m_xyz_z,m_lab_l,m_lab_a,m_lab_b); //--- Установка текущих параметров в поля ввода SetControls(0,false); }
И, наконец, нужен главный метод, в котором будут вызваться все вышеперечисленные методы для расчётов, отрисовки и установки значений компонент в поля ввода элемента. Здесь это метод CColorPicker::SetComponents(). Он тоже работает в двух режимах. В случае, если аргумент fix_selected равен true, то компоненты будут рассчитываться относительно выбранного цвета, а установка значений в поля ввода будет относительно выделенного радио-кнопкой компонента. Если же аргумент fix_selected равен false, то расчет идет относительно указанной цветовой модели. После всех расчётов цветовая палитра перерисовывается.
class CColorPicker : public CElement { private: //--- Расчёт и установка компонентов цвета void SetComponents(const int index,const bool fix_selected); }; //+------------------------------------------------------------------+ //| Расчёт и установка компонентов цвета | //+------------------------------------------------------------------+ void CColorPicker::SetComponents(const int index=0,const bool fix_selected=true) { //--- Если нужно скорректировать цвета относительно выделенного радио-кнопкой компонента if(fix_selected) { //--- Разложим на RGB-компоненты выбранный цвет m_rgb_r=m_clr.GetR(m_picked_color); m_rgb_g=m_clr.GetG(m_picked_color); m_rgb_b=m_clr.GetB(m_picked_color); //--- Конвертируем RGB-компоненты в HSL-компоненты m_clr.RGBtoHSL(m_rgb_r,m_rgb_g,m_rgb_b,m_hsl_h,m_hsl_s,m_hsl_l); //--- Корректировка компонент HSL AdjustmentComponentHSL(); //--- Конвертируем RGB-компоненты в LAB-компоненты m_clr.RGBtoXYZ(m_rgb_r,m_rgb_g,m_rgb_b,m_xyz_x,m_xyz_y,m_xyz_z); m_clr.XYZtoCIELab(m_xyz_x,m_xyz_y,m_xyz_z,m_lab_l,m_lab_a,m_lab_b); //--- Установим цвета в поля ввода SetControls(m_radio_buttons.SelectedButtonIndex(),true); return; } //--- Установка параметров цветовых моделей switch(index) { case 0 : case 1 : case 2 : SetHSL(); break; case 3 : case 4 : case 5 : SetRGB(); break; case 6 : case 7 : case 8 : SetLab(); break; } //--- Нарисовать палитру относительно выделенной радио-кнопки DrawPalette(m_radio_buttons.SelectedButtonIndex()); }
Для установки текущего цвета палитры, который будет присваиваться сразу всем объектам-маркерам, напишем метод CColorPicker::CurrentColor(). Далее в статье будет показано, где он будет использоваться.
class CColorPicker : public CElement { public: //--- Установка цвета выбранного пользователем цвета на палитре void CurrentColor(const color clr); }; //+------------------------------------------------------------------+ //| Установка текущего цвета | //+------------------------------------------------------------------+ void CColorPicker::CurrentColor(const color clr) { m_hover_color=clr; m_hover.Color(clr); m_hover.BackColor(clr); m_hover.Tooltip(::ColorToString(clr)); //--- m_picked_color=clr; m_picked.Color(clr); m_picked.BackColor(clr); m_picked.Tooltip(::ColorToString(clr)); //--- m_current_color=clr; m_current.BackColor(clr); m_current.Tooltip(::ColorToString(clr)); }
Все методы для расчётов готовы. Перейдем к методам обработки событий элемента.
Методы для обработки событий элемента
Для обработки событий и управления цветовой палитрой понадобятся следующие методы:
- Метод CColorPicker::OnHoverColor() — получение цвета (над палитрой) под курсором мыши. Программа выходит из метода, если курсор мыши вне области палитры. Если же курсор в её области, то определяем его координаты над ней и получаем цвет под курсором. Сразу же после этого новый цвет устанавливается предназначенному для этого маркеру, а с помощью метода ColorToString() графическим объектам маркера и цветовой палитре устанавливается всплывающая подсказка – строка цвета в формате RGB.
//+------------------------------------------------------------------+ //| Получение цвета под курсором мыши | //+------------------------------------------------------------------+ bool CColorPicker::OnHoverColor(const int x,const int y) { //--- Выйти, если фокус не на палитре if(!m_canvas.MouseFocus()) return(false); //--- Определим цвет на палитре под курсором мыши int lx =x-m_canvas.X(); int ly =y-m_canvas.Y(); m_hover_color=(color)::ColorToARGB(m_canvas.PixelGet(lx,ly),0); //--- Установим цвет и всплывающую подсказку в соответствующий образец (маркер) m_hover.Color(m_hover_color); m_hover.BackColor(m_hover_color); m_hover.Tooltip(::ColorToString(m_hover_color)); //--- Установим всплывающую подсказку палитре m_canvas.Tooltip(::ColorToString(m_hover_color)); return(true); }
- Метод CColorPicker::OnClickPalette() — обработка нажатия на цветовой палитре. В начале метода стоит проверка на имя объекта. Если нажатие было на палитре, то сохраняется и устанавливается цвет, который находится под курсором мыши, а также всплывающая подсказка для соответствующего маркера. В самом конце вызывается метод CColorPicker::SetComponents() для расчёта и установки компонентов цветовых моделей, относительно выделенного радио-кнопкой компонента.
//+------------------------------------------------------------------+ //| Обработка нажатия на цветовой палитре | //+------------------------------------------------------------------+ bool CColorPicker::OnClickPalette(const string clicked_object) { //--- Выйти, если имя объекта не совпадает if(clicked_object!=m_canvas.Name()) return(false); //--- Установим цвет и всплывающую подсказку в соответствующий образец (маркер) m_picked_color=m_hover_color; m_picked.Color(m_picked_color); m_picked.BackColor(m_picked_color); m_picked.Tooltip(::ColorToString(m_picked_color)); //--- Рассчитаем и установим компоненты цвета относительно выделенной радио-кнопки SetComponents(); return(true); }
- Метод CColorPicker::OnClickRadioButton() — обработка нажатия на радио-кнопке. Здесь сначала нужно пройти две проверки: (1) по идентификатору элемента и (2) по отображаемому тексту радио-кнопки. Если проверки пройдены, то цветовая палитра перерисовывается относительно выделенного компонента цветовой модели, которой он принадлежит.
//+------------------------------------------------------------------+ //| Обработка нажатия на радио-кнопке | //+------------------------------------------------------------------+ bool CColorPicker::OnClickRadioButton(const long id,const int button_index,const string button_text) { //--- Выйти, если идентификаторы не совпадают if(id!=CElement::Id()) return(false); //--- Выйти, если текст радио-кнопки не совпадает if(button_text!=m_radio_buttons.SelectedButtonText()) return(false); //--- Обновить палитру с учётом последних изменений DrawPalette(button_index); return(true); }
- Метод CColorPicker::OnEndEdit() — обработка ввода нового значения в поле ввода. Здесь достаточно только одной проверки — по идентификатору элемента, после прохождения которой осуществляется расчёт компонентов всех цветовых моделей относительно той, радио-кнопка компонента которой сейчас выделена.
//+------------------------------------------------------------------+ //| Обработка ввода нового значения в поле ввода | //+------------------------------------------------------------------+ bool CColorPicker::OnEndEdit(const long id,const int button_index) { //--- Выйти, если идентификаторы не совпадают if(id!=CElement::Id()) return(false); //--- Рассчитаем и установим компоненты цвета для всех цветовых моделей SetComponents(button_index,false); return(true); }
- Метод CColorPicker::OnClickButtonOK() — обработка нажатия на кнопке 'OK'. Это не окончательная версия метода, и далее в статье в него будет внесено небольшое дополнение. Единственное, что сейчас нужно знать, — то, что при нажатии на кнопку выбранный цвет сохраняется как текущий.
//+------------------------------------------------------------------+ //| Обработка нажатия на кнопке 'OK' | //+------------------------------------------------------------------+ bool CColorPicker::OnClickButtonOK(const string clicked_object) { //--- Выйти, если имя объекта не совпадает if(clicked_object!=m_button_ok.Text()) return(false); //--- Сохранить выбранный цвет m_current_color=m_picked_color; m_current.BackColor(m_current_color); m_current.Tooltip(::ColorToString(m_current_color)); return(true); }
- Метод CColorPicker::OnClickButtonCancel() — обработка нажатия на кнопке 'Cancel'. В этом методе только одна проверка по имени объекта. Затем, если форма, к которой присоединён элемент, имеет тип «диалоговое окно», то оно закрывается.
//+------------------------------------------------------------------+ //| Обработка нажатия на кнопке 'Cancel' | //+------------------------------------------------------------------+ bool CColorPicker::OnClickButtonCancel(const string clicked_object) { //--- Выйти, если имя объекта не совпадает if(clicked_object!=m_button_cancel.Text()) return(false); //--- Закроем окно, если оно диалоговое if(m_wnd.WindowType()==W_DIALOG) m_wnd.CloseDialogBox(); //--- return(true); }
Всего в обработчике событий цветовой палитры CColorPicker::OnEvent() будет шесть блоков. Каждый метод из списка выше будет вызываться по приходу предназначенного для него идентификатора события. Полный код обработчика событий элемента можно подробнее изучить в листинге кода ниже:
//+------------------------------------------------------------------+ //| Обработчик события графика | //+------------------------------------------------------------------+ void CColorPicker::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Обработка события перемещения курсора if(id==CHARTEVENT_MOUSE_MOVE) { //--- Выйти, если элемент скрыт if(!CElement::IsVisible()) return; //--- Координаты и состояние левой кнопки мыши int x=(int)lparam; int y=(int)dparam; m_mouse_state=(bool)int(sparam); CElement::MouseFocus(x>X() && x<X2() && y>Y() && y<Y2()); m_canvas.MouseFocus(x>m_canvas.X() && x<m_canvas.X2()-1 && y>m_canvas.Y() && y<m_canvas.Y2()-1); //--- Получение цвета под курсором мыши if(OnHoverColor(x,y)) return; //--- return; } //--- Обработка события нажатия левой кнопки мыши на объекте if(id==CHARTEVENT_OBJECT_CLICK) { //--- Если нажали на палитре if(OnClickPalette(sparam)) return; //--- return; } //--- Обработка ввода значения в поле ввода if(id==CHARTEVENT_CUSTOM+ON_END_EDIT) { //--- Проверка ввода нового значения if(OnEndEdit(lparam,(int)dparam)) return; //--- return; } //--- Обработка нажатия на элементе if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL) { //--- Если нажали на радио-кнопке if(OnClickRadioButton(lparam,(int)dparam,sparam)) return; //--- return; } //--- Обработка нажатия на переключателях полей ввода if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC || id==CHARTEVENT_CUSTOM+ON_CLICK_DEC) { //--- Проверка ввода нового значения if(OnEndEdit(lparam,(int)dparam)) return; //--- return; } //--- Обработка нажатия на кнопке элемента if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- Выйти, если идентификаторы не совпадают if(lparam!=CElement::Id()) return; //--- Если нажали на кнопке "OK" if(OnClickButtonOK(sparam)) return; //--- Если нажали на кнопке "CANCEL" if(OnClickButtonCancel(sparam)) return; //--- return; } }
Во многих элементах разрабатываемой библиотеки есть метод FastSwitching(). Обычно он используется для быстрой перемотки значений в полях ввода, прокрутки списков или таблиц. Однако здесь он нужен для перерисовки цветовой палитры, когда активирована перемотка того или иного счётчика поля ввода компонента. С кодом метода CColorPicker::FastSwitching() можно ознакомиться в приложенных к статье файлах.
Кнопка для вызова цветовой панели
Класс для создания цветовой палитры готов, но не хватает ещё одного элемента для полноценного её использования. Нужна кнопка, с помощью которой можно будет вызывать окно с цветовой палитрой. Кнопка должна быть устроена таким образом, чтобы на ней было видно, какой цвет в ней установлен в текущий момент. Напишем класс для создания такой кнопки. В списке ниже перечислены её составные части.
- Фон элемента
- Текстовая метка с описанием
- Индикатор выбранного цвета
- Фон кнопки
- Описание выбранного цвета в формате RGB
Рис. 3. Составные части кнопки для вызова цветовой палитры.
В предыдущих статьях уже были рассмотрены подобные элементы, поэтому не будем приводить здесь описание этого класса кода. Вместо этого сразу перейдём к части взаимодействия между кнопкой и цветовой палитрой. В приложенном к статье архиве вы можете найти файл ColorButton.mqh с классом CColorButton и самостоятельно изучить его более подробно.
Нам нужно связать цветовую палитру с кнопкой для её вызова. Сделаем это через указатель кнопки, который будет храниться в классе CColorPicker. Для этого подключим файл ColorButton.mqh к файлу ColorPicker.mqh и объявим экземпляр класса CColorButton, в котором будет сохраняться указатель на кнопку для вызова цветовой палитры.
//+------------------------------------------------------------------+ //| ColorPicker.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "SpinEdit.mqh" #include "SimpleButton.mqh" #include "RadioButtons.mqh" #include "ColorButton.mqh" //+------------------------------------------------------------------+ //| Класс для создания цветовой палитры для выбора цвета | //+------------------------------------------------------------------+ class CColorPicker : public CElement { private: //--- Указатель на кнопку, вызывающую элемент для выбора цвета CColorButton *m_color_button; };
Также понадобится публичный метод для сохранения указателя на кнопку. Чтобы максимально упростить взаимодействие кнопки вызова и палитры, при передаче объекта кнопки для сохранения её указателя здесь же всем маркерам палитры будет присваиваться текущий цвет кнопки. После этого откроется окно, к которому присоединена цветовая палитра (см. листинг кода ниже).
class CColorPicker : public CElement { public: //--- Сохраняет указатель на кнопку, вызывающую цветовую палитру void ColorButtonPointer(CColorButton &object); }; //+------------------------------------------------------------------+ //| Сохраняет указатель на кнопку, вызывающую цветовую палитру и | //| открывает окно, к которому палитра присоединена | //+------------------------------------------------------------------+ void CColorPicker::ColorButtonPointer(CColorButton &object) { //--- Сохранить указатель на кнопку m_color_button=::GetPointer(object); //--- Установим цвет переданной кнопки всем маркерам палитры CurrentColor(object.CurrentColor()); //--- Откроем окно, к которому присоединена палитра m_wnd.Show(); }
Кроме этого, нам понадобятся методы для установки и получения цвета кнопки. Установленный цвет будет отображаться на маркере (индикаторе) кнопки, а в качестве дополнительной информации в содержании текста кнопки отображается строковое представление цвета в RGB-формате.
class CColorButton : public CElement { public: //--- Возвращает/устанавливает текущий цвет параметра color CurrentColor(void) const { return(m_current_color); } void CurrentColor(const color clr); }; //+------------------------------------------------------------------+ //| Изменяет текущий цвет параметра | //+------------------------------------------------------------------+ void CColorButton::CurrentColor(const color clr) { m_current_color=clr; m_button_icon.BackColor(clr); m_button_label.Description(::ColorToString(clr)); }
И ещё одно небольшое дополнение добавляется в метод CColorPicker::OnClickButtonOK(). Если указатель на кнопку установлен, то:
- кнопке устанавливается выбранный на палитре цвет;
- окно, к которому присоединена цветовая палитра, закрывается;
- генерируется сообщение о том, что в палитре выбран новый цвет. Здесь понадобится новый идентификатор события ON_CHANGE_COLOR, находящийся в файле Defines.mqh. Также в этом сообщении будет содержаться (1) идентификатор элемента, (2) индекс элемента и (3) текст кнопки, которая вызвала цветовую палитру. Таким образом, в обработчике пользовательского класса можно будет понять, к какой кнопке относится это сообщение, что позволит правильно обработать событие;
- указатель на кнопку обнуляется.
Если же указателя на кнопку нет, то разработчику MQL-приложения в журнал терминала будет выведено сообщение с подсказкой.
//+------------------------------------------------------------------+ //| Обработка нажатия на кнопке 'OK' | //+------------------------------------------------------------------+ bool CColorPicker::OnClickButtonOK(const string clicked_object) { //--- Выйти, если имя объекта не совпадает if(clicked_object!=m_button_ok.Text()) return(false); //--- Сохранить выбранный цвет m_current_color=m_picked_color; m_current.BackColor(m_current_color); m_current.Tooltip(::ColorToString(m_current_color)); //--- Если есть указатель кнопки вызова окна для выбора цвета if(::CheckPointer(m_color_button)!=POINTER_INVALID) { //--- Установим кнопке выбранный цвет m_color_button.CurrentColor(m_current_color); //--- Закроем окно m_wnd.CloseDialogBox(); //--- Отправим сообщение об этом ::EventChartCustom(m_chart_id,ON_CHANGE_COLOR,CElement::Id(),CElement::Index(),m_color_button.LabelText()); //--- Обнулим указатель m_color_button=NULL; } else { //--- Если указателя нет и окно диалоговое, // вывести сообщение, что нет указателя на кнопку для вызова элемента if(m_wnd.WindowType()==W_DIALOG) ::Print(__FUNCTION__," > Невалидный указатель вызывающего элемента (CColorButton)."); } //--- return(true); }
Теперь у нас всё готово для того, чтобы протестировать цветовую палитру.
Тест элементов
Для теста может быть использован любой эксперт из предыдущей статьи. Сделаем копию и оставим в нём только главное меню и статусную строку. В главном окне (W_MAIN) графического интерфейса создадим пять кнопок для вызова диалогового окна (W_DIALOG) с цветовой палитрой. Другими словами, достаточно создать одну цветовую палитру на всё MQL-приложение. Каждый раз при нажатии на кнопку вызова диалогового окна с цветовой палитрой будет вызываться одно и то же окно. Но указатель кнопки в момент вызова нужно передавать в класс CColorPicker самостоятельно. Далее будет показано, как это должно быть реализовано в пользовательском классе приложения.
В пользовательском классе CProgram нужно объявить экземпляры классов для создания ещё одного окна (CWindow), пяти кнопок для вызова цветовой палитры (CColorButton) и цветовую палитру (CColorPicker), а также методы для их создания с отступами от крайней точки формы.
class CProgram : public CWndEvents { private: //--- Форма 2 - окно с цветовой палитрой для выбора цвета CWindow m_window2; //--- Кнопки для вызова окна с цветовой палитрой CColorButton m_color_button1; CColorButton m_color_button2; CColorButton m_color_button3; CColorButton m_color_button4; CColorButton m_color_button5; //--- Цветовая палитра CColorPicker m_color_picker; //--- private: //--- Форма 2 bool CreateWindow2(const string text); //--- Кнопки для вызова цветовой палитры #define COLORBUTTON1_GAP_X (7) #define COLORBUTTON1_GAP_Y (50) bool CreateColorButton1(const string text); #define COLORBUTTON2_GAP_X (7) #define COLORBUTTON2_GAP_Y (75) bool CreateColorButton2(const string text); #define COLORBUTTON3_GAP_X (7) #define COLORBUTTON3_GAP_Y (100) bool CreateColorButton3(const string text); #define COLORBUTTON4_GAP_X (7) #define COLORBUTTON4_GAP_Y (125) bool CreateColorButton4(const string text); #define COLORBUTTON5_GAP_X (7) #define COLORBUTTON5_GAP_Y (150) bool CreateColorButton5(const string text); //--- Цветовая палитра #define COLORPICKER_GAP_X (1) #define COLORPICKER_GAP_Y (20) bool CreateColorPicker(void); };
Коды методов создания кнопок для вызова цветовой палитры практически идентичны, поэтому приведём здесь пример только одного из них. Отличие может быть только в установке изначального цвета, который будет отображаться в индикаторе кнопки сразу после установки, а также текста в описании.
//+------------------------------------------------------------------+ //| Создаёт кнопку для вызова цветовой палитры 1 | //+------------------------------------------------------------------+ bool CProgram::CreateColorButton1(const string text) { //--- Сохраним указатель на окно m_color_button1.WindowPointer(m_window1); //--- Координаты int x=m_window1.X()+COLORBUTTON1_GAP_X; int y=m_window1.Y()+COLORBUTTON1_GAP_Y; //--- Установим свойства перед созданием m_color_button1.XSize(195); m_color_button1.YSize(18); m_color_button1.ButtonXSize(100); m_color_button1.ButtonYSize(18); m_color_button1.AreaColor(clrWhiteSmoke); m_color_button1.LabelColor(clrBlack); m_color_button1.BackColor(C'220,220,220'); m_color_button1.BorderColor(clrSilver); m_color_button1.CurrentColor(clrRed); //--- Создать элемент if(!m_color_button1.CreateColorButton(m_chart_id,m_subwin,text,x,y)) return(false); //--- Добавим указатель на элемент в базу CWndContainer::AddToElementsArray(0,m_color_button1); return(true); }
Код метода создания формы диалогового окна для цветовой палитры отличается от главного окна только тем, что нужно указать тип окна (W_DIALOG). Кроме этого, установим для этого окна уникальную картинку указывающую на его назначение. Все изображения приложены в архиве в конце статьи.
//+------------------------------------------------------------------+ //| Создаёт форму 2 для цветовой палитры | //+------------------------------------------------------------------+ #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\color_picker.bmp" //--- bool CProgram::CreateWindow2(const string caption_text) { //--- Сохраним указатель на окно CWndContainer::AddWindow(m_window2); //--- Координаты int x=(m_window2.X()>0) ? m_window2.X() : 30; int y=(m_window2.Y()>0) ? m_window2.Y() : 30; //--- Свойства m_window2.Movable(true); m_window2.XSize(350); m_window2.YSize(286); m_window2.WindowType(W_DIALOG); m_window2.WindowBgColor(clrWhiteSmoke); m_window2.WindowBorderColor(clrLightSteelBlue); m_window2.CaptionBgColor(clrLightSteelBlue); m_window2.CaptionBgColorHover(clrLightSteelBlue); m_window2.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\color_picker.bmp"); //--- Создание формы if(!m_window2.CreateWindow(m_chart_id,m_subwin,caption_text,x,y)) return(false); //--- return(true); }
В листинге ниже представлен код метода CProgram::CreateColorPicker() для создания цветовой палитры. Обязательно нужно сохранить указатель на диалоговое окно, к которому будем присоединять элемент. При добавлении указателя элемента в базу элементов нужно передать индекс окна, к которому элемент присоединён. В данном случае индекс диалогового окна [1].
//+------------------------------------------------------------------+ //| Создаёт цветовую палитру для выбора цвета | //+------------------------------------------------------------------+ bool CProgram::CreateColorPicker(void) { //--- Сохраним указатель на окно m_color_picker.WindowPointer(m_window2); //--- Координаты int x=m_window2.X()+COLORPICKER_GAP_X; int y=m_window2.Y()+COLORPICKER_GAP_Y; //--- Создание элемента if(!m_color_picker.CreateColorPicker(m_chart_id,m_subwin,x,y)) return(false); //--- Добавим указатель на элемент в базу CWndContainer::AddToElementsArray(1,m_color_picker); return(true); }
Нужно самостоятельно позаботиться о том, чтобы при нажатии на кнопку её указатель передавался цветовой палитре. Это можно сделать в обработчике событий пользовательского класса CProgram::OnEvent(). При нажатии на кнопку генерируется сообщение с идентификатором события ON_CLICK_BUTTON. В сообщении также содержится текст описания кнопки, по которому будем определять, какой именно объект кнопки типа CColorButton нужно передать цветовой палитре. Сразу же после передачи объекта кнопки откроется окно с цветовой палитрой, и во всех её маркерах будет отображаться цвет кнопки, объект которого только что был передан. В листинге кода ниже это продемонстрировано:
//+------------------------------------------------------------------+ //| Обработчик событий | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Событие нажатия на кнопке if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- Если нажали на первую кнопку if(sparam==m_color_button1.LabelText()) { m_color_picker.ColorButtonPointer(m_color_button1); return; } //--- Если нажали на вторую кнопку if(sparam==m_color_button2.LabelText()) { m_color_picker.ColorButtonPointer(m_color_button2); return; } //--- Если нажали на третью кнопку if(sparam==m_color_button3.LabelText()) { m_color_picker.ColorButtonPointer(m_color_button3); return; } //--- Если нажали на четвёртую кнопку if(sparam==m_color_button4.LabelText()) { m_color_picker.ColorButtonPointer(m_color_button4); return; } //--- Если нажали на пятую кнопку if(sparam==m_color_button5.LabelText()) { m_color_picker.ColorButtonPointer(m_color_button5); return; } } }
Уже после того, как выбор цвета подтвержден нажатием кнопки "ОК" на цветовой палитре, сообщение с идентификатором ON_CHANGE_COLOR будет обработано так, как показано в следующем листинге кода.
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Событие изменения цвета посредством цветовой палитры if(id==CHARTEVENT_CUSTOM+ON_CHANGE_COLOR) { //---Если идентификаторы элементов совпадают if(lparam==m_color_picker.Id()) { //--- Если ответ от первой кнопки if(sparam==m_color_button1.LabelText()) { //--- Изменить цвет объекта, который относится к первой кнопке... return; } //--- Если ответ от второй кнопки if(sparam==m_color_button2.LabelText()) { //--- Изменить цвет объекта, который относится ко второй кнопке... return; } //--- Если ответ от третьей кнопки if(sparam==m_color_button3.LabelText()) { //--- Изменить цвет объекта, который относится к третьей кнопке... return; } //--- Если ответ от четвёртой кнопки if(sparam==m_color_button4.LabelText()) { //--- Изменить цвет объекта, который относится к четвёртой кнопке... return; } //--- Если ответ от пятой кнопки if(sparam==m_color_button5.LabelText()) { //--- Изменить цвет объекта, который относится к пятой кнопке... return; } } return; } }
Скомпилируйте программу и загрузите её на график в терминале. Результат показан на скриншоте ниже:
Рис. 4. Тест кнопок для вызова цветовой палитры.
Нажав на кнопку для вызова цветовой палитры из пяти представленных на главном окне, откроется окно для выбора цвета (Color picker), как показано на следующем скриншоте:
Рис. 5. Тест элемента «Цветовая палитра для выбора цвета».
Теперь всё работает так, как задумывалось.
Заключение
В этой статье (первой главе девятой части серии) был представлен сложный составной элемент графического интерфейса — «Палитра для выбора цвета». В качестве дополнительного элемента создана специальная кнопка для вызова цветовой палитры. Теперь у пользователей разрабатываемой библиотеки появилась возможность управлять цветом того или иного объекта, используя графический интерфейс своего MQL-приложения.
В следующей статье будет представлено описание классов кода таких элементов, как «Индикатор выполнения» и «Линейный график».
Ниже вы можете загрузить к себе на компьютер весь материал девятой части серии, чтобы сразу получить возможность протестировать, как всё это работает. Если у вас возникают вопросы по использованию материала предоставленного в этих файлах, то вы можете обратиться к подробному описанию процесса разработки библиотеки в одной из статей в представленном списке ниже или задать вопрос в комментариях к статье.
Список статей (глав) девятой части:
- Графические интерфейсы IX: Элемент «Цветовая палитра для выбора цвета» (Глава 1)
- Графические интерфейсы IX: Элементы «Индикатор выполнения» и «Линейный график» (Глава 2)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Не совсем понял необходимость использования цветовой палитры в пользовательском приложении.
Зачем она нужна?
Если пользователь самостоятельно создает визуальную студию, то цветовая палитра будет является необходимым ему инструментом, но считать, что разработчики - одиночки, самостоятельно разработают визуальную студию и воспользуются предложенным Вами инструментом, по моему немного наивно. Для этого им придется значительно превзойти Ваши достижения. Это как собирать шатл в одиночку.)))
В остальном, смысл применения цветовой палитры для меня не ясен. Другое дело, если менются скины приложения или темы...
Это вам, одиночке, пытающемуся создать визуальную студию, непонятны аспекты и области применения цветовой палитры, а нам, простым смертным, уже приходилось разрабатывать себе колорпикеры и применять их в своих разработках. Теперь вот будет в одной библиотеке.
Конечно, можно постараться и найти применение этому инструменту в mql приложениях, но по настоящему его использование имеет смысл только в визуальной студии.
Предлагаю взять библиотеку автора как основу, и всем использующим ее разработчикам совместно занятся созданием визуальной студии, для создания интерфейсных mql приложений.
Если все разработчики скоординируют свою работу, то возможно, совместными усилиями что то может получится.
Конечно для координации совместной работы нужен руководитель, - главный разработчик, видящий план дальнейшего развития идеи и ее воплощения, но такой человек уже есть. По моему мнению, - это автор данной серии статей.
Кажется, идея конструктивная и многообещающая.)
P.S. Если проект совместного создания визуальной студии разработчиками будет начат, скоординирован и возглавлен, то обязательно присоединюсь к общей работе.
Ну, если Вам ясна необходимость применения колорпикера в приложении и нетрудно привести пример, буду благодарен.
Элементарно: для выбора цвета создаваемых приложением значков или линий в процессе работы. Зачем перезагружать программу, чтобы просто поменять цвет создаваемых объектов?
Андрей, ну вот зачем? :)))
Можно было предоставить человеку свободу для полёта мысли