Wrong window painting behaviour in frameless Qt window (win32) - c++

I have an QML application (also tested it with QWidgets, same problem) and to make it borderless (but still support the native WM features like aero snap, etc) I followed this by implementing an QAbstractNativeEventFilter and responding to the WM_NCCALSIZE signal with zero:
switch(msg->message) {
case WM_NCCALCSIZE:
*r = 0;
return 1;
...
}
I also set some window flags which are not in the Qt Namespace with
SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPSIBLINGS | WS_BORDER | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME);
This works fine until I move or resize the window which causes Qt to rerender and a unpainted area width the width of the title and the borders appears:
Before moving/resizing
After moving/resizing
I also found a workaround for this by adding the FramelessWindowHint flag in Qt:
window->setFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::Dialog);
But now this margins occurs again when the window state changes (maximizing, minimizing, ...). By blocking the WM_SIZE event for example when SIZE_MAXIMIZED is the parameter the margin doesn't appear but then I also am not able to maximize the window from Qt. This means it is a Qt side problem.
I also noticed by inspecting the window style with winspector, that after I have maximized it a new property atom appears:
Can you help me fixing this?

I think using SetWindowLong on your window handle and using the Qt Window Flags/Qt Widget Attributes is asking for trouble. You can go and look at the Qt source to see what happens when processing those window flags.
When I've created frameless windows, I've usually done it to prevent moving and resizing, because I am managing all that separately.
One problem that I had related to it, was when the On Screen Keyboard came up and docked, it would resize my windows. So besides calling resize() I had to also use setFixedSize to prevent my widget from getting manipulated when the operating system attempted to change the size of the window.
So in other words, I would add an application wide QShortcut, listening for the snapping keyboard shortcuts and resize your window the way you want it to when it happens, if you are managing a frameless window.
Hope that helps.

I wonder if this was a flaw of Qt message relay, coz I met with a similar problem which occurs on nested windows. If you press SIZE_MAXIMIZE or SIZE_MINIMIZE button of the parent window, the child window sometimes is not able to receive WM_SIZE message. I suppose there are roughly two solutions: 1. Fix Qt, 2. Work it around.
Here I have a OGL rendering child window, sometimes even the WM_SIZE message is not correctly passed. That is, if you resize parent window, you get part of the client area black.
I just use a simple workaround to fix this, which check the current size with the cached one, and make a sort of manually resize myself.

Related

WS_EX_TRANSPARENT and stylus events

I'm setting WS_EX_TRANSPARENT flag in window with the following code:
SetWindowLong( hwnd, GWL_EXSTYLE,WS_EX_TRANSPARENT | WS_EX_COMPOSITED | WS_EX_LAYERED );
This flag make that mouse events go to windows system instead of my app.
When I set this, I can't get Stylus events in hwnd window. Any way to solve that or that's not possible?
The behavior for the WS_EX_TRANSPARENT extended window style for Layered Windows is documented:
[I]f the layered window has the WS_EX_TRANSPARENT extended window style, the shape of the layered window will be ignored and the mouse events will be passed to other windows underneath the layered window.
Note: The rules for input handling are usually the same, for all pointing devices. Whenever you read "mouse events" you can expand that to "input events from a pointing device".
One workaround is given in the documentation for WS_EX_TRANSPARENT:
To achieve transparency without these restrictions, use the SetWindowRgn function.

Fullscreen mode for ActiveX control

I tried to implement a switch-to-fullscreen mode for an ActiveX control. This currently works by removing and hiding the parent window and changing my control's placement and position.
However, I have a problem with switching between applications while the control is in fullscreen mode. If I switch to another application and then click on my window area (not in the taskbar), it seems to not be activated. You can see in the taskbar, that another application still has the highlight and on the main screen, my window is partly hidden behind the taskbar unless it has the focus.
I process the WM_LBUTTONDOWN window message to detect if my window is clicked. And I already tried to call the following WINAPI functions:
::ShowWindow(m_hWnd, SW_RESTORE);
::SwitchToThisWindow(m_hWnd, FALSE);
::SetForegroundWindow(m_hWnd);
::SetActiveWindow(m_hWnd);
::SetFocus(m_hWnd);
::BringWindowToTop(m_hWnd);
::SetWindowPos(m_hWnd, HWND_TOP, m_monitorInfo.rcMonitor.left, m_monitorInfo.rcMonitor.top, m_monitorInfo.rcMonitor.right, m_monitorInfo.rcMonitor.bottom, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW); // SWP_NOACTIVATE, SWP_NOOWNERZORDER
I also tried to use ::SetWindowLongW(m_hWnd, GWL_STYLE, WS_VISIBLE); which surprisingly gave my window the focus back. But it then suddenly disappeared when trying to switch back from fullscreen mode again.
I have no idea why there are so many different functions that for me seem all to do roughly the same. But it doesn't matter as none of them worked anyway.
What is the/one correct way to behave my control correctly?
Windows might be getting a bit confused because you're filling the screen with a child window, but you want it to behave like a top-level (overlapped) window. The host application is probably not getting activated because you've hidden its window.
You might do better to create a new top-level full-screen window for full-screen mode. If this is owned by the top-level window that (ultimately) hosts your control then your new window will always be above it, so you don't need to hide the existing window. Activation should just work. In short, you want your window to behave like a pop-up modal dialog.
Adobe's flash player seems to do something similar. Full-screen playback is in a window of class ShockwaveFlashFullScreen.

What cause WS_TABSTOP to affect window painting order?

In WTL, CBitmapButton does not support picture with transparent layer, like PNG.
so i customize the code to use GDI+ to draw PNG, name it as CPNGButton.
But things are strange on win XP.
When the button is overlaped by other window, the button receives WM_PAINT first, and then the parent, causing incorrect result.
--->
I check the resource and remove WS_TABSTOP attribute from the template of the button, things go ok,parent window receives WM_PAINT first, then the button.
What cause WS_TABSTOP to affect window painting order?
This is what happens when you don't draw the background. That leaves a 'hole' in the window, you see the pixels of whatever window is behind yours. Or the desktop if there is no such window. Not otherwise sure what this has to do with WS_TABSTOP.
Use the WS_EX_TRANSPARENT style flag. That tells Windows that you want the parent of the button to draw itself in the client window to provide the background pixels.

flicker free tab control with WS_EX_COMPOSITED

I have a VS2008 C++ application for Windows XP SP3 developed using WTL 8.1. My application contains a tab control that flickers when the application border is resized.
My window hierarchy looks like this:
CFrameWindowImpl CMainFrm
|-CSplitterWindow Splitter
|-CTabView Configuration Tabs
| |-CDialogImpl Configuration View 1
| |-CDialogImpl Configuration View 2
| |-CDialogImpl Configuration View 3
|-CDialogImpl Control View
The solution I'm trying is to make the CFrameWindowImpl derived class use the WS_EX_COMPOSITED style and all windows beneath it use the WS_EX_TRANSPARENT style. Unfortunately, this makes the tab control buttons show as an empty black bar and the controls of any Configuration View to not show at all.
If I remove the WS_EX_COMPOSITED and WS_EX_TRANSPARENT styles, the form displays properly, but the CTabView and everything beneath it flickers horribly when resized.
What do I need to change to eliminate the flicker and draw the controls properly?
Thanks,
PaulH
Edit:
Got it working. I removed all the WS_EX_TRANSPARENT styles per Mark Ransom's suggestion. I put the WS_EX_COMPOSITED style on only the CTabCtrl (contained within the CTabView). Other controls get double-buffering as needed through WTL::CDoubleBufferImpl<>.
A window flickers because it gets erased before it's drawn. To eliminate this you need to disable erasing of the window entirely and use double buffering - draw the window contents into a bitmap, then copy the bitmap to the window. Because the bitmap contains the entire contents including the background, there's no need to erase anymore.
It looks like WS_EX_COMPOSITED will handle the double buffering automatically, but you still probably need to use a NULL background brush and/or handle the WM_ERASEBKGND message.
Whats not mentioned in MSDN is that the Desktop Window Manager - the component that hooks window painting on Windows Vista and 7 to perform the desktop composition necessary to get the aero glass effect - does NOT implement WS_EX_COMPOSITED.
Which means all the work you put into getting this style to work on XP, is doomed to become irrelevent on Vista or later.
The other problem with WS_EX_COMPOSITED - and why it was an optional style and not a default on XP: The double buffering only picks up painting performed during the BeginPaint / EndPaint block of the parent window. Lots of, even standard controls, perform painting outside of their WM_PAINT handlers, and as a result the backbuffer gets only partially painted.
Sadly, the result is, the only way to "eliminate" flicker in native API apps is to try to minimize it: WS_CLIPCHILDREN and WS_CLIPSIBLINGS can help if you dont have overlapping controls - to ensure that each control's area is painted only once. And ensure that the main dialog does not perform any flood filling in WM_ERASEBKGND
It is not, in my experience, possible to use double-buffering for anything that contains child controls (unless they all fully support WM_PRINT, which most do not).

windows beneath not being painted when using a layered window

I will try to explain my problem the best i can,
I'm creating a layered window in c++ (using windowsXP), all works fine until i drag my created window near the windows start button, and then when i press the star button of windows taskbar and close it again all the windows beneath of my layered window aren't being painted (only in the area of the start window that pops over my window).
My create window is like this:
CWnd::CreateEx( WS_EX_TOOLWINDOW |
WS_EX_LAYERED,
AfxRegisterWndClass(0),
lpstr_name, WS_POPUP, 0,0,0,0,
pc_parent->GetSafeHwnd(), 0);
...
When i create the window with this styles the problem ocurrs, but if i create with the extended style WS_EX_TRANSPARENT and all the others the problem does not occur any more. And if instead of a WS_POPUP window is a WS_CHILD or WS_OVERLAPPED then this also doesn't occur...
Can anyone please explain why when i create a WS_POPUP window with the WS_EX_LAYERED style all the beneath windows aren't updated, and if i add the style WS_EX_TRANSPARENT this works fine.
Note: why i do not use the WS_EX_TRANSPARENT style if it works right? if i use it then my window can not be dragged and i need it to do it :)
Updated:
alt text http://img17.imageshack.us/img17/586/clipboard01il.jpg
The image above is to describe better what is happening:
The first part of the image you can see my leyered window and beneath is the vs, in the second img i press the start button and then in the last image i already drag my layered window to the right and you can see that the vs window does not updates the affected area.
Note that this situation until now only occurs with the start window?! with other windows it does not happen!?...
Thanks
only in the area of the start window that pops over my window
That's expected. Only that clipping rectangle is obscured by the start menu so only that region will be repainted. What behavior are you expecting? If there are windows covered by more upper level windows, then they won't be repainted either -- why repaint something just to paint over it?
All underneath windows need to get repainted though if you use transparent because GDI can't calculate the final color of the pixel without knowing the area below the window's color.