如果我尝试在WM_NCCALCSIZE中插入客户端矩形,为什么我的客户端矩形绘图表现得很奇怪(提供图片)?

时间:2014-10-09 21:36:13

标签: c winapi gdi

我正在尝试使用原始Windows API在Windows上实现与Cocoa的NSPopover / GTK +的GtkPopover等效。到目前为止,我有大多数工作正常,但我不能让客户端矩形工作......以任何合理的方式。

如果我在下面注释掉我的WM_PAINT处理程序和我的WM_NCCALCSIZE,则会发生以下情况:

correct

现在,如果我启用WM_NCCALCSIZE,那么红色边框的一大块就会消失:

incorrect 1

如果我启用WM_PAINT,就会发生这种情况。请注意,上面的部分是黑色但应该是红色的部分保持黑色,但红色的部分现在是COLOR_ACTIVECAPTION

incorrect 2

图像来自葡萄酒。

Windows XP更糟糕:GetDCEx()调用(直接来自MSDN)失败,没有设置上一个错误代码,并将其更改为使用看似等效的GetWindowDC()结果透明正确红色边框,透明正确(!)红色边框,整个窗口分别具有标题栏颜色。葡萄酒产量不会改变。

我误解了WM_NCCALCSIZE的某些内容吗?我知道所有输入和输出都在相同的坐标空间中,所以我假设我可以只设置这些坐标并称之为一天。我确实尝试处理有效的矩形字段(rgrc[1] / rgrc[2]),通过将它们设置为空矩形并将rgrc[1]设置为新的客户端rect,但是这里都没有解决问题

MoveWindow()似乎也不是这样;如果我删除它并保持弹出窗口的初始大小,它仍然如上所示,具有相同的损坏的红色边框问题。

感谢。

这是代码。因为它是一个测试来弄清楚该怎么做,所以还没有错误检查。最终代码将进行完整的错误检查。

// 9 october 2014
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
// get Windows version right; right now Windows XP
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501       /* according to Microsoft's winperf.h */
#define _WIN32_IE 0x0600            /* according to Microsoft's sdkddkver.h */
#define NTDDI_VERSION 0x05010000    /* according to Microsoft's sdkddkver.h */
#include <windows.h>
#include <commctrl.h>
#include <stdint.h>
#include <uxtheme.h>
#include <string.h>
#include <wchar.h>
#include <windowsx.h>
#include <vsstyle.h>
#include <vssym32.h>

// #qo LIBS: user32 kernel32 gdi32

// TODO
// - investigate visual styles
// - put the client and non-client areas in the right place
// - make sure redrawing is correct (especially for backgrounds)
// - wine: BLACK_PEN draws a white line? (might change later so eh)
// - should the parent window appear deactivated?

HWND popover;

#define ARROWHEIGHT 8
#define ARROWWIDTH 8        /* should be the same for smooth lines */

LRESULT CALLBACK popoverproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC dc;
    HRGN region;
    POINT pt;
    RECT r;
    LONG width;
    LONG height;

    switch (uMsg) {
    case WM_NCPAINT:
        GetWindowRect(hwnd, &r);
        width = r.right - r.left;
        height = r.bottom - r.top;
        dc = GetDCEx(hwnd, (HRGN) wParam, DCX_WINDOW | DCX_INTERSECTRGN);
        if (dc == NULL) abort();
        BeginPath(dc);
        r.left = 0; r.top = 0;      // everything's in device coordinates
        pt.x = r.left;
        pt.y = r.top + ARROWHEIGHT;
        if (MoveToEx(dc, pt.x, pt.y, NULL) == 0) abort();
        pt.y += height - ARROWHEIGHT;
        if (LineTo(dc, pt.x, pt.y) == 0) abort();
        pt.x += width;
        LineTo(dc, pt.x, pt.y);
        pt.y -= height - ARROWHEIGHT;
        LineTo(dc, pt.x, pt.y);
        pt.x -= (width / 2) - ARROWWIDTH;
        LineTo(dc, pt.x, pt.y);
        pt.x -= ARROWWIDTH;
        pt.y -= ARROWHEIGHT;
        LineTo(dc, pt.x, pt.y);
        pt.x -= ARROWWIDTH;
        pt.y += ARROWHEIGHT;
        LineTo(dc, pt.x, pt.y);
        pt.x = 0;
        LineTo(dc, pt.x, pt.y);
        EndPath(dc);
        SetDCBrushColor(dc, RGB(255, 0, 0));
        region = PathToRegion(dc);
        FrameRgn(dc, region, GetStockObject(DC_BRUSH), 1, 1);
        SetWindowRgn(hwnd, region, TRUE);
        ReleaseDC(hwnd, dc);
        return 0;
    case WM_NCCALCSIZE:
        {
            RECT *r = (RECT *) lParam;
            NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *) lParam;

            if (wParam != FALSE)
                r = &np->rgrc[0];
            printf("%d | %d %d %d %d\n", wParam, r->left, r->top, r->right, r->bottom);
            r->left++;
            r->top++;
            r->right--;
            r->bottom--;
            r->top += ARROWHEIGHT;
            return 0;
        }
    case WM_ERASEBKGND:
        return (LRESULT) GetStockObject(HOLLOW_BRUSH);
    case WM_PAINT:
        dc = BeginPaint(hwnd, &ps);
        GetClientRect(hwnd, &r);
        FillRect(dc, &r, GetSysColorBrush(COLOR_ACTIVECAPTION));
        EndPaint(hwnd, &ps);
        return 0;
    }
    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
    case WM_COMMAND:
        if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == 100) {
            MoveWindow(popover, 50, 50,  200, 200, TRUE);
            ShowWindow(popover, SW_SHOW);
            UpdateWindow(popover);
            return 0;
        }
        break;
    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

int main(int argc, char *argv[])
{
    WNDCLASSW wc;
    HWND mainwin, button;
    MSG msg;

    ZeroMemory(&wc, sizeof (WNDCLASSW));
    wc.lpszClassName = L"popover";
    wc.lpfnWndProc = popoverproc;
    wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
    wc.style = CS_DROPSHADOW | CS_NOCLOSE;
    if (RegisterClassW(&wc) == 0)
        abort();
    popover = CreateWindowExW(WS_EX_TOPMOST,
        L"popover", L"",
        WS_POPUP,
        0, 0, 150, 100,
        NULL, NULL, NULL, NULL);
    if (popover == NULL)
        abort();

    ZeroMemory(&wc, sizeof (WNDCLASSW));
    wc.lpszClassName = L"mainwin";
    wc.lpfnWndProc = wndproc;
    wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
    if (RegisterClassW(&wc) == 0)
        abort();
    mainwin = CreateWindowExW(0,
        L"mainwin", L"Main Window",
        WS_OVERLAPPEDWINDOW,
        0, 0, 150, 100,
        NULL, NULL, NULL, NULL);
    if (mainwin == NULL)
        abort();
    button = CreateWindowExW(0,
        L"button", L"Click Me",
        BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
        20, 20, 100, 40,
        mainwin, (HMENU) 100, NULL, NULL);
    if (button == NULL)
        abort();
    ShowWindow(mainwin, SW_SHOWDEFAULT);
    if (UpdateWindow(mainwin) == 0)
        abort();
    while (GetMessageW(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
    return 0;
}

0 个答案:

没有答案