Текущий раздел:

Описания RDS

Руководство программиста

Глава 2. Создание моделей блоков

§2.12. Реакция блоков на действия пользователя

§2.12.2. Захват мыши, реакция на перемещение курсора

Полный исходный текст на языке C++ для библиотеки (DLL) с моделью блока, изображающего двухкоординатную рукоятку, которую пользователь может перемещать мышью.

      // Реакция на перемещение курсора мыши (п.2.12.2)
      #include <windows.h>
      #include <RdsDef.h>
      // Подготовка описаний сервисных функций
      #define RDS_SERV_FUNC_BODY GetInterfaceFunctions
      #include <RdsFunc.h>

      //========== Главная функция DLL ==========
      int WINAPI DllMain(HINSTANCE /*hinst*/,
                               unsigned long reason,
                               void* /*lpReserved*/)
      { if(reason==DLL_PROCESS_ATTACH) // Загрузка DLL
          { // Получение доступа к функциям RDS
            if(!GetInterfaceFunctions())
              RDS_SERV_ERROR_MSGW // Сообщение: старая версия RDS
          }
        return 1;
      }
      //========= Конец главной функции =========

      //====== Класс личной области данных ======
      class TSimpleJoystick
      { private:
          // Центр круга (рукоятки) до начала перетаскивания
          int OldHandleX,OldHandleY;
          // Координаты курсора на момент начала перетаскивания
          int OldMouseX,OldMouseY;
        public:
          // Настроечные параметры блока
          COLORREF BorderColor;       // Цвет рамки блока
          COLORREF FieldColor;        // Цвет прямоугольника
          COLORREF HandleColor;       // Цвет круга в покое
          COLORREF MovingHandleColor; // Цвет круга при таскании
          int HandleSize;             // Диаметр круга

          // Реакция на нажатие кнопки мыши
          int MouseDown(RDS_PMOUSEDATA mouse,double x,double y,DWORD *pFlags);
          // Реакция на перемещение курсора мыши
          void MouseMove(RDS_PMOUSEDATA mouse,double *px,double *py);
          // Рисование изображения блока
          void Draw(RDS_PDRAWDATA draw,double x,double y,BOOL moving);

          // Конструктор класса
          TSimpleJoystick(void)
            { BorderColor=0;          // Черная рамка
              FieldColor=0xffffff;    // Белое поле
              HandleColor=0xff0000;   // Синий круг
              MovingHandleColor=0xff; // Красный при таскании
              HandleSize=20;          // Диаметр круга
            };
      };
      //=========================================

      // Рисование изображения блока
      void TSimpleJoystick::Draw(RDS_PDRAWDATA draw,
                                 double x,double y,BOOL moving)
      { int hx,hy,cx,cy;
        RECT r;
        int hR=HandleSize*draw->DoubleZoom/2; // Радиус круга-рукоятки

        // Если размер блока - нулевой, рисовать негде
        if(draw->Height==0 || draw->Width==0)
          return;

        // Рисование поля блока
        rdsXGSetPenStyle(0,PS_SOLID,1,BorderColor,R2_COPYPEN);
        rdsXGSetBrushStyle(0,RDS_GFS_SOLID,FieldColor);
        rdsXGRectangle(draw->Left,draw->Top,
                       draw->Left+draw->Width,draw->Top+draw->Height);

        // Вычисление центра прямоугольника блока
        cx=draw->Left+draw->Width/2;
        cy=draw->Top+draw->Height/2;

        // Вычисление координат центра круга-рукоятки
        hx=cx+x*draw->Width/2;
        hy=cy-y*draw->Height/2;

        // Установка области отсечения
        r.left=draw->Left+1;
        r.top=draw->Top+1;
        r.right=draw->Left+draw->Width-1;
        r.bottom=draw->Top+draw->Height-1;
        rdsXGSetClipRect(&r);

        // Линии перекрестия
        rdsXGMoveTo(cx,draw->Top);
        rdsXGLineTo(cx,draw->Top+draw->Height);
        rdsXGMoveTo(draw->Left,cy);
        rdsXGLineTo(draw->Left+draw->Width,cy);

        // Рисование круга (цвет зависит от параметра moving)
        rdsXGSetPenStyle(RDS_GFSTYLE,PS_NULL,0,0,0);
        rdsXGSetBrushStyle(0,RDS_GFS_SOLID,
                           moving?MovingHandleColor:HandleColor);
        rdsXGEllipse(hx-hR,hy-hR,hx+hR+1,hy+hR+1);

        // Отмена отсечения
        rdsXGSetClipRect(NULL);
      }
      //=========================================

      // Реакция на нажатие кнопки мыши
      int TSimpleJoystick::MouseDown(RDS_PMOUSEDATA mouse,
                                     double x,double y,DWORD *pFlags)
      { int hx,hy,cx,cy,
            hR=HandleSize*mouse->DoubleZoom/2; // Радиус круга

        // Если размер - нулевой, реакция не имеет смысла
        if(mouse->Height==0 || mouse->Width==0)
          return RDS_BFR_DONE;
        // Если нажата не левая кнопка, перетаскивать не надо
        // Разрешаем в этом случае вызов контекстного меню блока
        if(mouse->Button!=RDS_MLEFTBUTTON)
          return RDS_BFR_SHOWMENU;

        // Координаты цента блока
        cx=mouse->Left+mouse->Width/2;
        cy=mouse->Top+mouse->Height/2;
        // Координаты центра круга-рукоятки
        hx=cx+x*mouse->Width/2;
        hy=cy-y*mouse->Height/2;

        // Проверка попадания курсора в круг
        if(abs(mouse->x-hx)<=hR && abs(mouse->y-hy)<=hR)
          { // Курсор попал в круг
            // Запоминаем координаты центр круга на момент
            // начала перетаскивания
            OldHandleX=hx;
            OldHandleY=hy;
            // Координаты курсора на начало перетаскивания
            OldMouseX=mouse->x;
            OldMouseY=mouse->y;
            // Взводим флаг захвата мыши
            *pFlags|=RDS_MOUSECAPTURE;
          }
        // Курсор не попал в рукоятку - захватывать мышь
        // и подготавливать перетаскивание не нужно
        return RDS_BFR_DONE;
      }
      //=========================================

      // Реакция на перемещение курсора мыши
      void TSimpleJoystick::MouseMove(RDS_PMOUSEDATA mouse,
                                      double *px,double *py)
      { int hx,hy,cx,cy;

        // Если размер - нулевой, реакция не имеет смысла
        if(mouse->Height==0 || mouse->Width==0)
          { *px=*py=0.0;
            return;
          }

        // Новые координаты центра рукоятки
        hx=OldHandleX+(mouse->x-OldMouseX);
        hy=OldHandleY+(mouse->y-OldMouseY);

        // Координаты центра блока
        cx=mouse->Left+mouse->Width/2;
        cy=mouse->Top+mouse->Height/2;

        // По новым координатам центра рукоятки вычисляем соответствующие
        // им вещественные значения выходов, ограничивая их
        // диапазоном [-1...1]
        *px=2.0*(hx-cx)/mouse->Width;
        if(*px>1.0) *px=1.0;
        else if(*px<-1.0) *px=-1.0;
        *py=-2.0*(hy-cy)/mouse->Height;
        if(*py>1.0) *py=1.0;
        else if(*py<-1.0) *py=-1.0;
      }
      //=========================================

      // Функция модели блока
      extern "C" __declspec(dllexport)
        int RDSCALL SimpleJoystick(int CallMode,
                RDS_PBLOCKDATA BlockData,
                LPVOID ExtParam)
      {
      // Макроопределения для статических переменных
      #define pStart ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define x (*((double *)(pStart+2*RDS_VSZ_S)))
      #define y (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D)))
        // Вспомогательная переменная - указатель на личную область,
        //приведенный к правильному типу
        TSimpleJoystick *data=(TSimpleJoystick*)(BlockData->BlockData);
        switch(CallMode)
          { // Инициализация
           case RDS_BFM_INIT:
              BlockData->BlockData=new TSimpleJoystick();
              break;
            // Очистка
            case RDS_BFM_CLEANUP:
              delete data;
              break;
            // Проверка допустимости типов переменных
            case RDS_BFM_VARCHECK:
              return strcmp((char*)ExtParam,"{SSDD}")?
                RDS_BFR_BADVARSMSG:RDS_BFR_DONE;
            // Нажатие кнопки мыши
            case RDS_BFM_MOUSEDOWN:
              return data->MouseDown((RDS_PMOUSEDATA)ExtParam,x,y,
                &(BlockData->Flags));
            // Отпускание кнопки мыши
            case RDS_BFM_MOUSEUP:
              // Снятие захвата мыши
              RDS_SETFLAG(BlockData->Flags,RDS_MOUSECAPTURE,FALSE);
              break;
            // Перемещение курсора мыши
            case RDS_BFM_MOUSEMOVE:
              // Проверка: включен ли захват мыши
              if(BlockData->Flags & RDS_MOUSECAPTURE)	// Включен
                { // Вызываем функцию реакции
                  data->MouseMove((RDS_PMOUSEDATA)ExtParam,&x,&y);
                  Ready=1; // Взводим сигнал готовности
                }
              break;
            // Рисование
            case RDS_BFM_DRAW:
              data->Draw((RDS_PDRAWDATA)ExtParam,x,y,
              BlockData->Flags & RDS_MOUSECAPTURE);
              break;
          }
        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef y
      #undef x
      #undef Ready
      #undef Start
      #undef pStart
      }
      //=========================================