WinAPI - бескомпромиссное окно без полей с тенью под ним - PullRequest
0 голосов
/ 13 апреля 2020

Чего я хочу добиться:

Я хочу создать окно, которое ведет себя как WS_OVERLAPPEDWINDOW | WS_SIZEBOX (может быть свернуто, развернуто, закрыто из системного контекстного меню на Alt+Space, поддерживает Окно Snapping , имеет тень, если оно включено в системе Performance Options -> [x] Enable shadow under window и т. Д.). Оно также должно быть без полей (без строки заголовка, значка, имени приложения в строке заголовка и т. Д.), Просто из области клиента и ничего больше.

Итак, основные цели:

  • Имеет без рамки;
  • Поддерживает функциональность окна по умолчанию;
  • Отбрасывает тень внизу.

Не говорите, что это невозможно. Самый простой пример такого окна - Telegram Desktop. Они используют библиотеку Qt. Не говори, что я должен использовать это. Я хочу узнать, как они сделали этот трюк. Их окно без границ, без ошибок (которые описаны ниже). Я знаю, что Qt использует OpenGL внутри для своего окна. Но я также знаю, что рисование с помощью GDI + возможно только без OpenGL.

Что я уже пробовал:

  • github: melak47 / BorderlessWindow Это окно имеет крошечную границу вокруг. Вы можете увидеть это, если вы действительно быстро измените его размер, удерживая за верхний или левый край окна. Мы также можем установить поля как MARGINS m{0,0,0,1} для DwmExtendFrameIntoClientArea(hWnd, &m);. Но окно все равно останется той же границей, которую можно увидеть, если изменить размер.

  • github: rossy / borderless-window Это решение лучше. Он предлагает два варианта борьбы с этой проблемой.

    1. Граница здесь скрыта за счет включения WS_EX_LAYERED стиля окна с цветовой маркировкой по цвету (например, пурпурный). Но очевидно, что теперь нам нужно выполнять проверки каждый раз, когда мы рисуем что-то на растровом изображении для указанного цвета цветности. Если пользователь dr aws clear magenta RGB(255, 0, 255), мы можем заменить его на RGB(254, 0, 255), чтобы избежать отверстий внутри. Но я думаю, что это очень плохое решение, которое вызывает ненужные проверки для каждого пикселя в растровом изображении.

    2. Граница теперь считается частью клиентской области. Мы отключаем WM_EX_LAYERED и цветовые манипуляции и используем GDI + для рисования прозрачных цветов на верхней границе. Это то, что вы не можете нарисовать чистый непрозрачный цвет на этой границе (например, graphics.FillRect(&Gdiplus::SolidBrush{Gdiplus::Color{255, 255, 0, 0}}, Gdiplus::Rect{0, 0, windowWidht, windowHeight}) не будет покрывать верхнюю границу красным цветом - граница будет белого или другого цвета (я не знаю, от чего это зависит, но я думаю, это предпочтение цвета оконной системы, например Personalize -> Colors -> [x] Title bars and window borders)). Кстати, вы можете покрыть его любым цветом, который хотите, просто установив непрозрачность этого цвета на 254. Это будет работать. Но проблема все еще существует: если мы попытаемся изменить размер окна, угадайте, что .. у нас есть эта белая граница сверху (или то, что вы установили, когда MARGINS m{0,0,0,1} для DwmExtendFrameIntoClientArea(hWnd, &m);. А также обратите внимание, что это будет очень больно, когда вы забудете, что не можете нарисовать чистый непрозрачный цвет и что-то нарисовать с помощью GDI. Поэтому я прихожу к следующему решению.

  • My Идея состоит в том, чтобы рисовать со значением альфа 254 только самую верхнюю линию сканирования растрового изображения. Поскольку я также выполняю двойную буферизацию, я могу сделать это с AlphaBlend(hdc, 0, 0, width, 1, memHdc, 0, 0, width, 1, {AC_SRC_OVER, 0, 254, AC_SRC_ALPHA});. Но это решение все еще имеет свою собственную ошибку. Когда окно изменения размера мы все еще получили эту границу потому что он рисуется быстрее, чем содержимое нашего окна. Также у нас появляются странные пиксели с левой стороны, если мы изменяем размеры окна по верхнему краю. И последняя ошибка заключается в том, что эта альфа иногда исчезает, а верхняя граница видна не только в невыгруженном состоянии. клиентские регионы, но также и над подтвержденными регионами.

    код WM_PAINT процедуры :

    LRESULT wmPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
    {
        PAINTSTRUCT ps = {};
        HDC hdc = BeginPaint(hWnd, &ps);

        auto [width, height] = dimensions.normal;

        HDC memHdc = CreateCompatibleDC(hdc);
        HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height);
        SelectObject(memHdc, hBitmap);

        // Children perform their drawings on "back buffer" - memDc
        handleChildrenDraw(memDc);


        // Copy top line as transparent and other as always to paint over non-client area as it was client

        BLENDFUNCTION bf = {};
        bf.BlendOp = AC_SRC_OVER;
        bf.SourceConstantAlpha = 254;
        bf.AlphaFormat = AC_SRC_ALPHA;
        auto alphaSuccess = AlphaBlend(hdc, 0, 0, width, 1, memHdc, 0, 0, width, 1, bf);
        expect(alphaSuccess == TRUE);
        auto copySuccess = BitBlt(hdc, 1, 0, width, height, memHdc, 1, 0, SRCCOPY);
        expect(copySuccess == TRUE);

        EndPaint(hWnd, &ps);

        DeleteObject(memHdc);
        DeleteObject(hBitmap);

        return 0;
    }

Sreensho t: окно отображается с белым надписью сверху (по каким-то причинам граница была отображена)

Вопрос:

Подскажите, пожалуйста, как исправить эту ошибку при рисовании верхней границы или Есть ли лучшее решение для рисования без полей окна с WinAPI? Заранее спасибо.

...