Search code examples
winapidraw

How am i supposed to draw colored rectangles


The rectangles are supposed to be randomly colored when I draw them with the left button. They have to be filled with color not only the border I have tried to use pens and brushes but it doesn't work. I think that it is because of the method I am using to draw them. But I don't know how to do it otherwise. 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;
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);
}

Solution

  • Right now you're primarily missing two things:

    1. where you draw your rectangles, you aren't creating and selecting a brush, so the default brush is being used to fill your rectangles.
    2. Where you handle WM_LBUTTONUP, you're not invalidating the rectangle after you add it to your vector of rectangles to draw. So it doesn't get drawn (until you do something that forces it to be re-drawn like minimizing and restoring it).

    That's pretty much all you're missing through.

    So at the end of your WM_LBUTTONUP handler, you can replace your call to UpdateWindow, (which isn't necessary here) with a call to InvalidateRect, something like this: InvalidateRect(hWnd, add, true);. Note that this only invalidates the rectangle that we just drew (where your other code passes NULL for the invalid rectangle, so it invalidates everything). Only invalidating the part that's really invalid can improve efficiency (somewhat).

    And in your WM_PAINT handler, you want code something like this:

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
    
        // For the moment, I'm just hard-coding a fairly bright red fill:
        HBRUSH brush = CreateSolidBrush(RGB(192, 0, 0));
        HBRUSH original = (HBRUSH)SelectObject(hdc, brush);
    
        // TODO: Add any drawing code that uses hdc here...
        for (auto& rc : vRect)
        {
            Rectangle(hdc,
                rc.left,
                rc.top,
                rc.right,
                rc.bottom
            );
        }
        // restore original brush before releasing the DC:
        SelectObject(hdc, original);
        EndPaint(hWnd, &ps);
    
        // And destroy the brush:
        DeleteObject(brush);
    }
    break;