Stela Nikolaeva
Stela Nikolaeva

Reputation: 15

Draw, Select and Drag Rectangle WinApi

I have a problem with dragging the rectangles and also with selecting them.

I have to draw multiple rectangles (with the left mouse button), then select one of them and drag it all around the window (with the right mouse button). But when I select it, it doesn't move.

The problem with the selection - when I try to select them in descending order (of the way a draw them) they are selected all.

Here is my code:

POINT LeftButtonDown;
POINT ptCurrent;
POINT ptClicked;
vector<CRect> vRect;
bool isRubberBand = false;
bool IsClicked(CRect r, int x, int y);

void select(HWND hWnd, CRect r);
void deselect(HWND hWnd, CRect r);
void DrawRubberBand(HWND hWnd);
void MoveFromTo(HDC hdc, CRect rectSelected, POINTS anchor, POINTS now);

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
CRect add;
CRect rectSelected;

HDC             hdc;
static POINTS   anchor;

switch (message)
{
case WM_COMMAND:
{
    int wmId = LOWORD(wParam);
    // Parse the menu selections:
    switch (wmId)
    {
    case IDM_ABOUT:
        DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
        break;
    case IDM_EXIT:
        DestroyWindow(hWnd);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
}
break;

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);
    // TODO: Add any drawing code that uses hdc here...
    for (auto& rc : vRect)
    {
        Rectangle(hdc,
            rc.left,
            rc.top,
            rc.right,
            rc.bottom
        );
    }
    EndPaint(hWnd, &ps);
}
break;

case WM_LBUTTONDOWN:
{
    LeftButtonDown.x = LOWORD(lParam); //Start point
    LeftButtonDown.y = HIWORD(lParam);

    isRubberBand = true;

    ptCurrent.x = LOWORD(lParam); //current point
    ptCurrent.y = HIWORD(lParam);

    DrawRubberBand(hWnd); //draw the rect
}
break;

case WM_LBUTTONUP: // adding to a vector
{
    if (!isRubberBand)
    {
        return 0;
    }
    isRubberBand = false;

    //InvalidateRect(hWnd, NULL, TRUE);
    add.SetRect(LeftButtonDown.x, LeftButtonDown.y, ptCurrent.x, ptCurrent.y); //setting the coords of the rect
    vRect.push_back(add); //add the rect
    UpdateWindow(hWnd);
}
break;

case WM_MOUSEMOVE:

    if (wParam & MK_LBUTTON) //drawing the rectangle
    {
        hdc = GetDC(hWnd);
        if (!isRubberBand)
        {
            break;
        }
        DrawRubberBand(hWnd);

        ptCurrent.x = LOWORD(lParam);
        ptCurrent.y = HIWORD(lParam);

        DrawRubberBand(hWnd);

    }

    if (wParam & MK_RBUTTON) //dragging the rectangle
    {
        hdc = GetDC(hWnd);

        POINTS now = MAKEPOINTS(lParam);
        SetROP2(hdc, R2_NOTXORPEN);
        MoveFromTo(hdc,rectSelected,anchor,now);
        InvalidateRect(hWnd, NULL, TRUE);

        anchor = MAKEPOINTS(lParam);

        ReleaseDC(hWnd, hdc);

    }
    break;

case WM_RBUTTONDOWN:
{
    int x{ LOWORD(lParam) }, y{ HIWORD(lParam) };

    for (auto& rc : vRect)
    {
        if (IsClicked(rc, x, y))
        {
            select(hWnd, rc);
            rectSelected= rc;
            anchor = MAKEPOINTS(lParam);

            break;
        }
        else {
            deselect(hWnd, rc);
        }
    }
    //select-deselect

}
break;
case WM_RBUTTONUP:
    break;
case WM_DESTROY:
    PostQuitMessage(0);
    break;
default:
    return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}


bool IsClicked(CRect r, int x, int y)
{
    POINT pt{ x, y };
    return (bool)PtInRect(&r, pt);
}
void select(HWND hWnd, CRect r) {
    HDC hdc = GetDC(hWnd);
    HPEN selectPen = CreatePen(PS_DOT, 0, RGB(255, 0, 0));
    SelectObject(hdc, selectPen);
    Rectangle(hdc, r.left, r.top, r.right, r.bottom);
    DeleteObject(selectPen);
}
void deselect(HWND hWnd, CRect r) {
    HDC hdc = GetDC(hWnd);
    Rectangle(hdc, r.left, r.top, r.right, r.bottom);
}

void DrawRubberBand(HWND hWnd) {
    HDC hdc = GetDC(hWnd);

    SetROP2(hdc, R2_NOT);
    SelectObject(hdc, GetStockObject(NULL_BRUSH));
    Rectangle(hdc,
        LeftButtonDown.x,
        LeftButtonDown.y,
        ptCurrent.x,
        ptCurrent.y
    );
    ReleaseDC(hWnd, hdc);
  }
  void MoveFromTo(HDC hdc, CRect rectSelected, POINTS anchor, POINTS now) 
  {
    Rectangle(hdc, rectSelected.left, rectSelected.top, rectSelected.right, rectSelected.bottom);
    rectSelected.left = rectSelected.left + now.x - anchor.x; //x
    rectSelected.top = rectSelected.top + now.y - anchor.y;    //y
    rectSelected.right = rectSelected.right + now.x - anchor.x; //x
    rectSelected.bottom = rectSelected.bottom + now.y - anchor.y; //y
    Rectangle(hdc, rectSelected.left, rectSelected.top, rectSelected.right, rectSelected.bottom);
   }

Upvotes: 0

Views: 504

Answers (1)

Zeus
Zeus

Reputation: 3890

The reason why all rectangles are selected in descending order is that the selected rectangle is found in WM_RBUTTONDOWN and the loop is breaked, resulting in the subsequent deselection of the rectangle is not performed.

There are several reasons why the rectangle cannot be dragged:

  1. The MoveFromTo function should pass the reference of rectSelected, otherwise the coordinates of the modified rectangle in the function cannot be applied to the actual rectangle.

  2. You need to find the rectangle before dragging, delete it from vRect, and then add the dragged rectangle to vRect, so as to ensure that the coordinates of the rectangle are updated every time in WM_PAINT.

Here is the code :

POINT LeftButtonDown;
POINT ptCurrent;
POINT ptClicked;
vector<CRect> vRect;
bool isRubberBand = false;
bool IsClicked(CRect r, int x, int y);

void select(HWND hWnd, CRect r);
void deselect(HWND hWnd, CRect r);
void DrawRubberBand(HWND hWnd);
void MoveFromTo(HDC hdc, CRect &rectSelected, POINTS anchor, POINTS now);

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    CRect add;
    static CRect rectSelected;
    TCHAR s[100] = L"";
    HDC             hdc;
    static POINTS   anchor;

    switch (message)
    {
    case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: Add any drawing code that uses hdc here...
        for (auto& rc : vRect)
        {
            Rectangle(hdc,
                rc.left,
                rc.top,
                rc.right,
                rc.bottom
            );
        }
        EndPaint(hWnd, &ps);
    }
    break;

    case WM_LBUTTONDOWN:
    {
        LeftButtonDown.x = LOWORD(lParam); //Start point
        LeftButtonDown.y = HIWORD(lParam);

        isRubberBand = true;

        ptCurrent.x = LOWORD(lParam); //current point
        ptCurrent.y = HIWORD(lParam);

        DrawRubberBand(hWnd); //draw the rect
    }
    break;

    case WM_LBUTTONUP: // adding to a vector
    {
        if (!isRubberBand)
        {
            return 0;
        }
        isRubberBand = false;

        //InvalidateRect(hWnd, NULL, TRUE);
        add.SetRect(LeftButtonDown.x, LeftButtonDown.y, ptCurrent.x, ptCurrent.y); //setting the coords of the rect
        vRect.push_back(add); //add the rect
        UpdateWindow(hWnd);
    }
    break;

    case WM_MOUSEMOVE:

        if (wParam & MK_LBUTTON) //drawing the rectangle
        {
            hdc = GetDC(hWnd);
            if (!isRubberBand)
            {
                break;
            }
            DrawRubberBand(hWnd);

            ptCurrent.x = LOWORD(lParam);
            ptCurrent.y = HIWORD(lParam);

            DrawRubberBand(hWnd);

        }
        if (wParam & MK_RBUTTON) //dragging the rectangle
        {
            hdc = GetDC(hWnd);
            POINTS now = MAKEPOINTS(lParam);
            if (PtInRect(&rectSelected, POINT{ now.x,now.y }))
            {
                SetROP2(hdc, R2_NOTXORPEN);
                MoveFromTo(hdc, rectSelected, anchor, now);
                InvalidateRect(hWnd, NULL, TRUE);
                anchor = MAKEPOINTS(lParam);
            }

            ReleaseDC(hWnd, hdc);
        }
        break;

    case WM_RBUTTONDOWN:
    {
        bool isFind = false;
        int x{ LOWORD(lParam) }, y{ HIWORD(lParam) };
        for (auto& rc : vRect)
        {
            if (!isFind && IsClicked(rc, x, y))
            {
                select(hWnd, rc);
                rectSelected = rc;
                anchor = MAKEPOINTS(lParam);
                isFind = TRUE;
            }
            else {
                deselect(hWnd, rc);
            }
        }
        //select-deselect
    }
    break;
    case WM_RBUTTONUP:
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}


bool IsClicked(CRect r, int x, int y)
{
    POINT pt{ x, y };
    return (bool)PtInRect(&r, pt);
}
void select(HWND hWnd, CRect r) {
    HDC hdc = GetDC(hWnd);
    HPEN selectPen = CreatePen(PS_DOT, 0, RGB(255, 0, 0));
    SelectObject(hdc, selectPen);
    Rectangle(hdc, r.left, r.top, r.right, r.bottom);
    DeleteObject(selectPen);
}
void deselect(HWND hWnd, CRect r) {
    HDC hdc = GetDC(hWnd);
    Rectangle(hdc, r.left, r.top, r.right, r.bottom);
}

void DrawRubberBand(HWND hWnd) {
    HDC hdc = GetDC(hWnd);

    SetROP2(hdc, R2_NOT);
    SelectObject(hdc, GetStockObject(NULL_BRUSH));
    Rectangle(hdc,
        LeftButtonDown.x,
        LeftButtonDown.y,
        ptCurrent.x,
        ptCurrent.y
    );
    ReleaseDC(hWnd, hdc);
}
void MoveFromTo(HDC hdc, CRect &rectSelected, POINTS anchor, POINTS now)
{
    Rectangle(hdc, rectSelected.left, rectSelected.top, rectSelected.right, rectSelected.bottom);
    for (auto it = vRect.begin();it != vRect.end();it++)
    {
        if (*it == rectSelected)
        {
            vRect.erase(it);
            break;
        }
    }
    rectSelected.left = rectSelected.left + now.x - anchor.x; //x
    rectSelected.top = rectSelected.top + now.y - anchor.y;    //y
    rectSelected.right = rectSelected.right + now.x - anchor.x; //x
    rectSelected.bottom = rectSelected.bottom + now.y - anchor.y; //y
    Rectangle(hdc, rectSelected.left, rectSelected.top, rectSelected.right, rectSelected.bottom);
    vRect.push_back(rectSelected);
}

It works for me:

enter image description here

Upvotes: 2

Related Questions