Resize. Часть 2

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

Перетаскивание объекта

Сделаем сначала возможность перетаскивание компонента. Как было оговорено ранее, это будет реализовано в классе TResize. Перетаскивание объекта можно реализовать самым простым способом – переопределением некоторых событий мыши (Down, Move и Up). Но тут возникает проблема, так как сам компонент, с которым мы работает, объявлен типа TControl, а события мыши у него закрыты (в protected).

Эту проблему можно решить привидением к типу, лежащему ниже в иерархии классов, но этот будет довольно сложно реализовать, так как придется определять, является ли компонент TGraphicControl или TWinControl. Поэтому проще самому ввести класс, наследника TControl, в котором открыть события мыши. Назовем его TWinCntrl, и уже к нему будет приводить наш компонент.

  1. class TWinCtrl : public TWinControl {
  2. __published:
  3.     __property OnMouseMove;
  4.     __property OnMouseUp;
  5.     __property OnMouseDown;
  6. };

Для реализации перетаскивания компонента сначала добавим следующие private поля:
    FDrag : bool – флаг, обозначающий перетаскивают ли сейчас объект.
    FOldTop, FOldTop: int – положение курсора на объекте до начала перетаскивания.

  1. bool FDrag;
  2. int FOldTop;
  3. int FOldLeft;

Так же добавим в качестве методов в класс TResize, обработчики событий мыши:

  1. void __fastcall MouseDown( TObject *Sender, TMouseButton Button,
  2.     TShiftState Shift, int X, int Y );
  3. void __fastcall MouseMove( TObject *Sender, TShiftState Shift, int X,
  4.     int Y );
  5. void __fastcall MouseUp( TObject *Sender, TMouseButton Button,
  6.     TShiftState Shift, int X, int Y );

И в конечном итоге, объявление класса будет выглядеть так:

  1. class TResize {
  2. private:
  3.     TControl *FObject;
  4.     TMarker *FMarkers[ 8 ];
  5.     bool FDrag;
  6.     int FOldTop;
  7.     int FOldLeft;
  8.  
  9. public:
  10.     __property TControl *Object = {
  11.         read = FObject, write = SetObject
  12.     };
  13.     void PositionMarkers( );
  14.     void HideMarkers( );
  15.     void ShowMarkers( );
  16.  
  17.     void SetObject( TControl *setObject );
  18.  
  19.     void __fastcall MouseDown( TObject *Sender, TMouseButton Button,
  20.         TShiftState Shift, int X, int Y );
  21.     void __fastcall MouseMove( TObject *Sender, TShiftState Shift, int X,
  22.         int Y );
  23.     void __fastcall MouseUp( TObject *Sender, TMouseButton Button,
  24.         TShiftState Shift, int X, int Y );
  25.  
  26.     TResize( TComponent *AOwner );
  27. };

Для начала внесем некоторые изменения в уже написанный код. В конструкторе класса проинициализируем FDrag, а в методе SetObject, используя привидение к типу TWinCntrl, зададим обработчики событий мыши для объекта.

  1. TResize::TResize( TComponent *AOwner ) {
  2.     FDrag = false;
  3.  
  4.     for ( int i = 0; i < 8; i++ ) {
  5.         FMarkers[ i ] = new TMarker( AOwner, i, this );
  6.     }
  7.  
  8.     HideMarkers( );
  9. }
  10.  
  11. //----------------------------------------------------------------------
  12.  
  13. void TResize::SetObject( TControl *setObject ) {
  14.     FObject = setObject;
  15.     PositionMarkers( );
  16.     TWinCtrl *tmpObj = static_cast<TWinCtrl*>( FObject );
  17.     tmpObj->OnMouseMove = MouseMove;
  18.     tmpObj->OnMouseUp = MouseUp;
  19.     tmpObj->OnMouseDown = MouseDown;
  20. }

Теперь можно перейти написанию самих обработчиков.

Перетаскивание объекта. MouseDown

Событие при нажатии на кнопку мыши, если она находится над объектом. Начинаем перетаскивание и запоминаем координаты курсора.

  1. void __fastcall TResize::MouseDown( TObject *Sender, TMouseButton Button,
  2.     TShiftState Shift, int X, int Y ) {
  3.     FDrag = true;
  4.     FOldLeft = X;
  5.     FOldTop = Y;
  6. }

Перетаскивание объекта. MouseMove

Событие при перемещении мыши. Определяем, начато ли перетаскивание, и если да, то скрываем маркеры и новые координаты объекта увеличиться на текущие координаты мыши за вычетом сохраненных координат, полученных при Mouse Down.

  1. void __fastcall TResize::MouseMove( TObject *Sender, TShiftState Shift,
  2.     int X, int Y ) {
  3.     if ( FDrag ) {
  4.         HideMarkers( );
  5.  
  6.         FObject->Left = FObject->Left + X - FOldLeft;
  7.         FObject->Top = FObject->Top + Y - FOldTop;
  8.     }
  9. }

Перетаскивание объекта. MouseUp

Заканчиваем перетаскивание и заново позиционируем маркеры.

  1. void __fastcall TResize::MouseUp( TObject *Sender, TMouseButton Button,
  2.     TShiftState Shift, int X, int Y ) {
  3.     FDrag = false;
  4.     PositionMarkers( );
  5. }

На этом с перетаскиванием объекта можно закончить. Можно запустить тестовый проект и посмотреть, что получилось…

Изменение размеров компонента.

Теперь перейдем, к тому, что собственно, нам и надо: изменение размера компонента при перемещении маркеров.
Для начала добавим в класс TMarker следующие поля:

  1. bool FDoResize; // Флаг, определяющий возможность изменения размеров
  2. // Положение курсора на маркере до начала перетаскивания.   
  3. int FOldLeft;
  4. int FOldTop;
  5. // Размеры и положение объекта до ресайза
  6. int FOldObjectWidth;
  7. int FOldObjectHeight;
  8. int FOldObjectLeft;
  9. int FOldObjectTop;

И так же обработчики событий мыши:

  1. void __fastcall MouseDown( TObject *Sender, TMouseButton Button,
  2.     TShiftState Shift, int X, int Y );
  3. void __fastcall MouseMove( TObject *Sender, TShiftState Shift, int X,
  4.     int Y );
  5. void __fastcall MouseUp( TObject *Sender, TMouseButton Button,
  6.     TShiftState Shift, int X, int Y );

И в итоге, объявление класса будет выглядеть так:

  1. class TMarker : public TPanel {
  2. private:
  3.     TResize *FResize;
  4.     TControl *FObject;
  5.     int FNumber;
  6.    
  7.     bool FDoResize; // Флаг, определяющий возможность изменения размеров
  8.     // Положение курсора на маркере до начала перетаскивания.   
  9.     int FOldLeft;
  10.     int FOldTop;
  11.     // Размеры и положение объекта до ресайза
  12.     int FOldObjectWidth;
  13.     int FOldObjectHeight;
  14.     int FOldObjectLeft;
  15.     int FOldObjectTop;
  16.  
  17. public:
  18.     void __fastcall MouseDown( TObject *Sender, TMouseButton Button,
  19.         TShiftState Shift, int X, int Y );
  20.     void __fastcall MouseMove( TObject *Sender, TShiftState Shift, int X,
  21.         int Y );
  22.     void __fastcall MouseUp( TObject *Sender, TMouseButton Button,
  23.         TShiftState Shift, int X, int Y );
  24.  
  25.     TMarker( TComponent *AOwner, int setNumber, TResize *setResize );
  26. };

Для начала в конструктор TMarker добавим следующие строки.

  1. FDoResize = false;
  2. OnMouseDown = MouseDown;
  3. OnMouseMove = MouseMove;
  4. OnMouseUp = MouseUp;

Как видно, тут задаются обработчики событий мыши для маркера и инициализируются FDoResize и FObject.

Теперь перейдем непосредственно к самим обработчикам событий мыши:

Resize. MouseDown.

Сначала из TResize мы получаем объект, с которым работаем. Потом запоминаем его прежние размеры и положение. Так же для реализации перетаскивания маркера запоминаем координаты мыши. Начинаем ресайз.

  1. void __fastcall TMarker::MouseDown( TObject *Sender, TMouseButton Button,
  2.     TShiftState Shift, int X, int Y ) {
  3.     FObject = FResize->Object;
  4.  
  5.     FOldObjectWidth = FObject->Width;
  6.     FOldObjectHeight = FObject->Height;
  7.     FOldObjectLeft = FObject->Left;
  8.     FOldObjectTop = FObject->Top;
  9.  
  10.     FOldLeft = X;
  11.     FOldTop = Y;
  12.     FDoResize = true;
  13. }

Resize. MouseMove.

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

  1. void __fastcall TMarker::MouseMove( TObject *Sender, TShiftState Shift, int X,
  2.     int Y ) {
  3.     if ( FDoResize ) {
  4.         FResize->HideMarkers( );
  5.  
  6.         switch( FNumber ) {
  7.         case 0:
  8.             Left = Left + X - FOldLeft;
  9.             Top = Top + Y - FOldTop;
  10.             FObject->Left = Left;
  11.             FObject->Width = FOldObjectWidth + FOldObjectLeft - FObject->Left;
  12.             FObject->Top = Top;
  13.             FObject->Height = FOldObjectHeight + FOldObjectTop - FObject->Top;
  14.             break;
  15.         case 1:
  16.             Top = Top + Y - FOldTop;
  17.             FObject->Top = Top;
  18.             FObject->Height = FOldObjectHeight + FOldObjectTop - Top;
  19.             break;
  20.         case 2:
  21.             Left = Left + X - FOldLeft;
  22.             Top = Top + Y - FOldTop;
  23.             FObject->Width = Left - FObject->Left;
  24.             FObject->Top = Top;
  25.             FObject->Height = FOldObjectHeight + FOldObjectTop - Top;
  26.             break;
  27.         case 3:
  28.             Left = Left + X - FOldLeft;
  29.             FObject->Width = Left - FObject->Left;
  30.             break;
  31.         case 4:
  32.             Left = Left + X - FOldLeft;
  33.             Top = Top + Y - FOldTop;
  34.             FObject->Width = Left - FObject->Left;
  35.             FObject->Height = Top - FObject->Top;
  36.             break;
  37.         case 5:
  38.             Top = Top + Y - FOldTop;
  39.             FObject->Height = Top - FObject->Top;
  40.             break;
  41.         case 6:
  42.             Left = Left + X - FOldLeft;
  43.             Top = Top + Y - FOldTop;
  44.             FObject->Left = Left;
  45.             FObject->Width = FOldObjectWidth + FOldObjectLeft - FObject->Left;
  46.             FObject->Height = Top - FObject->Top;
  47.             break;
  48.         case 7:
  49.             Left = Left + X - FOldLeft;
  50.             FObject->Left = Left;
  51.             FObject->Width = FOldObjectWidth + FOldObjectLeft - FObject->Left;
  52.             break;
  53.         }
  54.     }
  55. }

Resize. MouseUp.

Тут все просто. Завершаем ресайз и показываем маркеры.

  1. void __fastcall TMarker::MouseUp( TObject *Sender, TMouseButton Button,
  2.     TShiftState Shift, int X, int Y ) {
  3.     FDoResize = false;
  4.     FResize->PositionMarkers( );
  5. }

На этом, в общем-то цикл статей можно завершить. Запустим тестовый проект, и посмотрим, как все красиво перетаскивается :)

Скачать исходник

builder, cpp

Комментарии (1)

Петров Дмитрий | 24 ноября 2010 г. 20:25 Ответить

можно реализовать динамическое добавление компонентов формы
(базовые - TPanel, TEdit, TEdit...)
хранить информацию о компонентах в XML.. и подгружать при запуске (для простоты ориентирования использовать Node->Attributes "Class")
как вариант.. для сохранения..

_di_IXMLNode Elements = XMLDocument1->DocumentElement;
for (int i = 0; i < ComponentCount; i++) {
AnsiString _compName = (Form1->Components[i]->Name);
// ---- Edit
TEdit* edit;
edit = dynamic_cast<TEdit*>(Components[i]);
if(edit){
_di_IXMLNode EditNode = Elements->ChildNodes->FindNode(_compName);
if(EditNode){ // Replace
//EditNode->Attributes[WideString("Name")] = WideString(_compName);
EditNode->Attributes[WideString("Name")] = WideString(edit->Name);
EditNode->Attributes[WideString("Top")] = WideString(edit->Top);
EditNode->Attributes[WideString("Left")] = WideString(edit->Left);
else { // Add New
Elements = XMLDocument1->DocumentElement->AddChild(WideString(edit->Name));
Elements->Attributes[WideString("Class")] = WideString(edit->ClassName());
Elements->Attributes[WideString("Name")] = WideString(_compName);
Elements->Attributes[WideString("Top")] = WideString(edit->Top);
Elements->Attributes[WideString("Left")] = WideString(edit->Left);

Очень полезный ресурс... все просто и грамотно написано...

Добавить комментарий

  • Допустимые html-теги:
    <b> </b> - жирный шрифт
    <em> </em> - наклонный
    <s> </s> - зачеркнутый
    <pre> </pre> - сохранение отступов (печать кода)
    [?]
Введите текст с картинки