Home > Mobile >  How to change textout() when it's already drawn?
How to change textout() when it's already drawn?

Time:09-22

I'm trying to move static TCHAR greeting[] = _T("123"); by using arrows, but it doesn't move at all. MessageBox is used as a confirmation of getting keyboard input.

void move(HWND hWnd, WPARAM wParam, LPARAM lParam, unsigned int *x, unsigned int * y) {
    RECT rt;
    if (wParam == VK_LEFT) {
        GetClientRect(hWnd, &rt);
        x = x - 5;
        InvalidateRect(hWnd, &rt, FALSE);
        MessageBox(hWnd, LPCSTR("123"), LPCSTR("123"), MB_OK);
        
    }
    if (wParam == VK_DOWN) {
        GetClientRect(hWnd, &rt);
        y = y - 5;
        InvalidateRect(hWnd, &rt, FALSE);
        

    }
    if (wParam == VK_UP) {
        GetClientRect(hWnd, &rt);
        y = y   5;
        InvalidateRect(hWnd, &rt, FALSE);
        
    }
    if (wParam == VK_RIGHT) {
        GetClientRect(hWnd, &rt);
        x = x   5;
        InvalidateRect(hWnd, &rt, FALSE);
        
    }
}
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
        PAINTSTRUCT ps;
        HDC hdc;
        static TCHAR greeting[] = _T("123");
        unsigned int x = 50;
        unsigned int y = 50;

        switch (message)
        {
        case WM_PAINT: {
            hdc = BeginPaint(hWnd, &ps);
            
            TextOut(hdc,
                x, y,
                greeting, 3);


            EndPaint(hWnd, &ps);
            break;
        }

        case WM_KEYDOWN:
            move(hWnd, wParam, lParam, &x, &y);
            break;
        case WM_DESTROY: {
            PostQuitMessage(0);
            break;
        default: {
            return DefWindowProc(hWnd, message, wParam, lParam);
            break;
        }

        }
                       return 0;
        }
}

Can somebody explain, what is wrong here? I'm still trying to learn about <Windows.h> so pls don't try to make the code much more complicated.

CodePudding user response:

The x and y variables you are using to draw the text with are local to WndProc and are always reset to the initial values whenever a new message is received. You need to move their declarations outside of WndProc() (or at least make them static) so that their values can persist between messages. You can then update their values in your WM_KEYDOWN handler, and use their current values to draw the text in your WM_PAINT handler.

Also, your move() function is updating the pointers that are pointing at the x and y variables, it is not updating the values of the x and y variables themselves.

Try this instead:

void move(HWND hWnd, WPARAM wParam, LPARAM lParam, unsigned int *x, unsigned int * y) {
    switch (wParam) {
        case VK_LEFT:
            *x -= 5;
            InvalidateRect(hWnd, NULL, FALSE);
            break;

        case VK_DOWN:
            *y -= 5;
            InvalidateRect(hWnd, NULL, FALSE);
            break;

        case VK_UP:
            *y  = 5;
            InvalidateRect(hWnd, NULL, FALSE);
            break;

        case VK_RIGHT:
            *x  = 5;
            InvalidateRect(hWnd, NULL, FALSE);
            break;
    }
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    static TCHAR greeting[] = _T("123");
    static unsigned int x = 50;
    static unsigned int y = 50;

    switch (message) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            
            TextOut(hdc,
                x, y,
                greeting, 3);

            EndPaint(hWnd, &ps);
            break;
        }

        case WM_KEYDOWN:
            move(hWnd, wParam, lParam, &x, &y);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}

CodePudding user response:

While @Remy Lebeau's answer is right, I don't think I'd write the move function quite the way he has. I'd pass x and y by reference instead of using pointers, and I'd consolidate more of the code together. Oh, and since it doesn't use lParam, we might as well not pass it at all:

void move(HWND hWnd, WPARAM wParam, unsigned int &x, unsigned int & y) {
    switch (wParam) {
        case VK_LEFT:
            x -= 5;
            break;

        case VK_DOWN:
            y -= 5;
            break;

        case VK_UP:
            y  = 5;
            break;

        case VK_RIGHT:
            x  = 5;
            break;

        default: return;
    }
    InvalidateRect(hWnd, NULL, FALSE);
}

Also note, however that this has a little bit of a problem. Passing FALSE as the last parameter to InvalidateRect means it doesn't erase the background. If you move your string around much, you're probably going to end up with artifacts of the movement--fragments of the string showing up in both the previous and the current location.

One easy way to fix that is change the final parameter to TRUE. To make that a bit more efficient, you can calculate the rectangle covering the old and new locations of the string, and invalidate only that much. Alternatively, invalidate the old rectangle with TRUE and the new rectangle with FALSE, and Windows will figure things out from there.

Another possibility (slightly more work, but under the right circumstances it can pay off) is to create an off-screen bitmap containing both your message and a border in the background color that's large enough to cover up the message in its previous location. Then when you move it around, you just blit that bitmap to the screen.

That obviously only works if your background is a solid, uniform color though.

  •  Tags:  
  • c
  • Related