Отправляет email-рассылки с помощью сервиса Sendsay

Borland C++ Builder - всякая всячина

  Все выпуски  

Borland C++ Builder всякая всячина (№13. Глумление над кнопкой)


Служба Рассылок Subscribe.Ru

Приветствую всех получателей рассылки Borland C++ Builder - всякая всячина!

№13. Глумление над кнопкой

 

"...А Фагот, спровадив пострадавшего конферансье, объявил публике так:
- Таперича, когда этого надоедалу сплавили, давайте откроем дамский магазин!
И тотчас пол сцены покрылся персидскими коврами, возникли громадные зеркала, с боков освещенные зеленоватыми трубками, а меж зеркал витрины, и в них зрители в веселом ошеломлении увидели разных цветов и фасонов парижские женские платья..."

(М.А.Булгаков "Мастер и Маргарита")

 

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

Исходя из условия поставленной задачи делаем вывод, что нам будет необходимо ввести в компонент новое свойство типа TFont, отвечающее за характеристики шрифта, которым отображается заголовок кнопки в тот момент, когда над ней находится курсор мыши. Для этого в файле-заголовке компонента MyButton.h в секции private: объявления класса введем описание трех переменных и объявления четырех функций, в секции protected: - одну функцию, а в секции __published: - два новых свойства (все это выделено красным цветом):

//---------------------------------------------------------------------------
 
class PACKAGE TMyButton : public TSpeedButton
{
private:
    bool IsFocused;
    TFont *StoredFont;
    TFont *FFocusFont;
    TFont * __fastcall GetFocusFont(void);
    void __fastcall SetFocusFont(TFont *AFocusFont);
    TFont * __fastcall GetFont(void);
    void __fastcall SetFont(TFont *AFont);
protected:
    virtual void __fastcall WndProc(Messages::TMessage &Message);
public:
    __fastcall TMyButton(TComponent* Owner);
    __fastcall ~TMyButton();
__published:
    __property TFont *FocusFont= { read= GetFocusFont, write= SetFocusFont };
    __property TFont *Font= { read= GetFont, write= SetFont };
};
//---------------------------------------------------------------------------

Переменная IsFocused будет являться индикатором нахождения курсора над кнопкой. Поскольку на время нахождения курсора над кнопкой мы будем менять свойства шрифта кнопки Font, нам нужно будет где-то сохранять его старое значение. Введем для этой цели внутреннюю переменную StoredFont. Переменная FFocusFont будет у нас хранилищем для свойства FocusFont.

Теперь - про свойства: свойство FocusFont читает и записывает значения не напрямую из (в) переменной (-ую) FFocusFont, а использует для этого объявленные выше функции GetFocusFont и SetFocusFont. Смысл такого решения я поясню чуть позже. Кроме того мы ввели еще одно очень интересное свойство - Font. Очень интересное оно потому, что перекрывает одноименное свойство класса TSpeedButton, используя для чтения и записи своего значения соответственно функции GetFont и SetFont, объявленные в секции private:. О смысле этого свойства - также чуть позже.

И, наконец, поясню значение функции WndProc: она перекрывает одноименную виртуальную функцию класса TControl, которая в свою очередь является ни чем иным, как обработчиком сообщений компонента. То есть, все сообщения системы, адресуемые компоненту, передаются в качестве параметра Message этому методу. Он волен изменить сообщение (параметр передается по ссылке), отреагировать на его появление какими-нибудь действиями или, в конце концов, передать его на обработку методу WndProc наследуемого класса. Кроме того, аналогичного результата можно достичь, записав в свойство WindowProc (наследуемого также от класса TControl) имя любой другой функции, объявленной с такими-же параметрами вызова, что и WndProc. Это позволит вам менять обработчики сообщений как перчатки прямо во время работы компонента.


Ну, а теперь - немного поломаем файл реализации MyButton.cpp. Прежде всего подправим описания конструктора и деструктора компонента:

//===========================================================================
 
__fastcall TMyButton::TMyButton(TComponent* Owner)
    : TSpeedButton(Owner)
{
    IsFocused= false;
    StoredFont= new TFont();
    FFocusFont= new TFont();
    if (FFocusFont!=NULL) FFocusFont->Assign(TSpeedButton::Font);
}
//---------------------------------------------------------------------------
 
__fastcall TMyButton::~TMyButton()
{
    FFocusFont->Free();
    StoredFont->Free();
}
//===========================================================================

Конструктор нашего компонента в первую очередь инициализирует переменную-индикатор IsFocused, затем создает два объекта типа TFont и, если создание объектов прошло успешно (указатель на объект отличен от нуля), присваивает значения свойств стандартного шрифта кнопки (класса TSpeedButton) свойствам шрифта, введенного нами (в класс TMyButton). Деструктор действует еще более убого. Он просто втупую уничтожает порожденные конструктором объекты, описывающие шрифты. Все, в общем, довольно тривиально. Да, преимущества использования метода Free для уничтожения объекта вместо использования оператора delete я описывал еще в первом выпуске, так что повторяться не будем.

Теперь рассмотрим реализацию функций чтения и записи значений свойств шрифтов:

//===========================================================================
 
TFont * __fastcall TMyButton::GetFocusFont(void)
{
    if (IsFocused) return TSpeedButton::Font;
    else return FFocusFont;
}
//---------------------------------------------------------------------------
 
void __fastcall TMyButton::SetFocusFont(TFont *AFocusFont)
{
    FFocusFont->Assign(AFocusFont);
    if (IsFocused) TSpeedButton::Font->Assign(FFocusFont);
}
//---------------------------------------------------------------------------
 
TFont * __fastcall TMyButton::GetFont(void)
{
    if (IsFocused) return StoredFont;
    else return TSpeedButton::Font;
}
//---------------------------------------------------------------------------
 
void __fastcall TMyButton::SetFont(TFont *AFont)
{
    if (!IsFocused) TSpeedButton::Font->Assign(AFont);
    else StoredFont->Assign(AFont);
}
//===========================================================================

Функция GetFocusFont вызывается не только при чтении характеристик шрифта, но и при их записи. Это легко отследить при отладке, установив на ее первый оператор метку прерывания. При записи характеристик она возвращает указатель на свойство Font класса-предка TSpeedButton в том случае, если курсор находится на кнопкой (IsFocused==true). То есть, в этом случае мы меняем сразу характеристики шрифта, используемого системой для отображения заголовка. Если же курсор находится над другим объектом, мы подставляем в качестве шрифта-приемника указатель на объект-хранилище свойства FocusFont - FFocusFont.

SetFocusFont вызывается относительно редко, только при прямом присваивании значения одного указателя на шрифт другому указателю на шрифт. На самом деле в нашей реализации происходит не присваивание нового адреса указателю, а копирование всех свойств объекта, определяемого указателем-параметром. В дополнение к присвоению новых свойств объекту FFocusFont она еще и устанавливает для "системного" шрифта все свойства этого объекта-шрифта в том случае, если курсор находится в границах кнопки.

Функцмя GetFont работает аналогично функции GetFocusFont. Если IsFocused==true, она возвращает указатель на объект, временно хранящий характеристики "системного" шрифта. После того, как курсор "покинет" кнопку, мы вернем "системному" шрифту сохраненные во временном объекте характеристики. Если же IsFocused==false, функция возвращает указатель непосредственно на объект "системного" шрифта класса TSpeedButton.

И, наконец, функция SetFont: если курсор не находится над кнопкой (то есть для отображения заголовка используется "родной", "системный" шрифт), функция копирует свойства объекта, адрес которого передается в параметре, в объект-шрифт класса TSpeedButton, иначе же она сохраняет эти свойства в промежуточном объекте "временного хранения".

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

И у нас остался только один метод - обработчик событий компонента. Вот его код:

//===========================================================================
 
void __fastcall TJButton::WndProc(Messages::TMessage &Message)
{
    if (Message.Msg==CM_MOUSEENTER)
    {
      IsFocused= true;
      StoredFont->Assign(TSpeedButton::Font);
      TSpeedButton::Font->Assign(FFocusFont);
    }
    else
    {
      if (Message.Msg==CM_MOUSELEAVE)
      {
        IsFocused= false;
        FFocusFont->Assign(TSpeedButton::Font);
        TSpeedButton::Font->Assign(StoredFont);
      }
    }
    TSpeedButton::WndProc(Message);
}
//===========================================================================

Как я уже говорил, этот метод получает в качестве параметра структуру (по ссылке), описывающую параметры сообщения, присланного системой. Из всех параметров нам понадобится только идентификатор сообщения Msg. С помощью него мы должны будем выделить два типа сообщений: CM_MOUSEENTER и CM_MOUSELEAVE. Первое из них "приходит", когда курсор попадает в пределы границ нашей кнопки, второй - когда курсор покидает эти границы. Кстати, идентификаторы всех подобных сообщений вы можете подглядеть в файле ($BCB)\Include\VCL\controls.hpp.

Итак, если нам "пришло" сообщение CM_MOUSEENTER, мы устанавливаем переменную-идентификатор IsFocused в true (вот она где, собака, порылась), а затем сохраняем характеристики "оригинального" шрифта кнопки, наследуемого от класса TSpeedButton, во временном объекте StoredFont и присваиваем шрифту новые характеристики от объекта-хранилища свойства FocusFont. При постеплении сообщения CM_MOUSELEAVE делаем все то же самое, но с точностью до наоборот, то есть IsFocused - в false, из "оригинального" шрифта обновляем объект FFocusedFont и возвращаем из временного хранилища старые характеристики "оригинальному" шрифту.

В любом случае, после обработки (удачной или нет - неважно в данном примере) сообщения мы передаем данное сообщение на обработку унаследованному от TSpeedButton обработчику сообщений, пусть он сам разбирается, что с ними делать дальше. Хотя, бывают ситуации, когда после анализа сообщение удобнее "погасить", то есть не вызывать штатный (наследуемый) обработчик, например, если вы анализируете собственноручно введенное в систему нестандартное сообщение.

И это все... Откомпилируйте и установите по-новой ваш пакет, как это было описано в десятом выпуске (удалять предварительно старую версию из палитры компонент не надо!). Если вы таким образом не обновите версию пакета, сделанные изменения не отразятся в вашем проекте, использующим компоненты из этого пакета. После этого, по-хорошему, надо-бы сделать полный "build" всему проекту, но, как правило, достаточно бывает сделать не "build", а "make" (Ctrl+F9), Билдер автоматически перекомпилирует файл, использующий измененный компонент и перелинкует проект.

Обратите внимание, у кнопки TMyButton в вашем проекте появилось новое свойство в Object Inspector: FocusFont. Изменив его характеристики также, как вы меняете характеристики штатного шрифта (свойство Font), вы измените стиль отображения заголовка кнопки при наведении на нее курсора мыши. Только, я вас умоляю, не надо злоупотреблять этим параметром: постарайтесь не менять более одной характеристки, например, цвета шрифта или его подчеркивания или его "жирности". В противном случае вы только ухудшите положение и вместо профессионального подхода получите, извините за выражение, "ламерский" подход к созданию интерфейса.

Вот на этом мы и остановимся пока...

PS: да, если вы найдете в рассылке ошибки или очепятки, критикуйте смело письмом по моему адресу...



С уважением, Васильев Евгений...

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу
Рейтингуется SpyLog

В избранное