A frameless and translucent QWidget with WS_THICKFRAME
-
In mfc/win32, usually use updatelayeredwindow to implement translucent window. The window can have WS_THICKFRAME and WS_CAPTION properties. The System animation of minimized depends WS_CAPTION. The System animation of auto maximum depends WS_THICKFRAME.
But in Qt, it only can set FramelessWindowHint flag.
This is YY written by Qt. --link removed--
Spy indicate YY have WS_EX_LAYERED, WS_THICKFRAME, WS_CAPTION. I don't know how it works with WS_THICKFRAME?[andreyc: looks like spam or phishing message. I removed a link]
-
[quote author="zeljko" date="1399727234"]QWidget::winID is windows HWND of your window, so you can try to handle it directly via win api.[/quote]
I tried to handle QWidget directly via win api. But this leads to another problem. The QWidget's interface can't refresh after maximized.
Here is a sample code.
@
test2.h
#include <QWidget>class test2 : public QWidget
{
Q_OBJECTpublic:
test2(QWidget *parent);
~test2();public slots:
void OnBtnClicked();protected:
bool nativeEvent( const QByteArray & eventType, void * message, long * result );
void paintEvent(QPaintEvent*);
bool event(QEvent * event);// wm msg handler
bool HandleGetMinMaxInfoMsg(MSG *msg, long *result);private:
};
@@ test2.cpp
#include "test2.h"
#include <Windows.h>
#include <QEvent>
#include <QDebug>
#include <QDesktopWidget>
#include <windowsx.h>
#include <QWindow>
#include <QSurfaceFormat>
#include <QPushButton>
#include <QPainter>
#include <QApplication>test2::test2(QWidget *parent)
: QWidget(parent)
{
QPushButton *pbn = new QPushButton("test", this);
pbn->move(100, 100);
connect(pbn, SIGNAL(clicked()), this, SLOT(OnBtnClicked()));setWindowFlags(Qt::FramelessWindowHint | Qt::Window );
setAttribute(Qt::WA_TranslucentBackground);}
test2::~test2()
{}
bool test2::event(QEvent * e)
{
if(e->type() == QEvent::WinIdChange)
{
HWND hWnd = (HWND)this->winId();
LONG lStyle = ::GetWindowLong(hWnd, GWL_STYLE);
::SetWindowLong(hWnd, GWL_STYLE, lStyle | WS_OVERLAPPED | WS_THICKFRAME);
}
return QWidget::event(e);
}bool test2::nativeEvent( const QByteArray & eventType, void * message, long * result )
{
MSG *msg = (MSG *)message;
const int captionHeight = 32;
const int frameWidth = 5;switch (msg->message )
{case WM_GETMINMAXINFO :
HandleGetMinMaxInfoMsg(msg, result);
*result = 0;
return true;
}return QWidget::nativeEvent(eventType, message, result);
}void test2::paintEvent( QPaintEvent* )
{
QPainter painter(this);HWND hWnd = (HWND)this->winId();
RECT winRect;
::GetWindowRect(hWnd, &winRect);
QRect qWinRect = QRect(0, 0, winRect.right - winRect.left, winRect.bottom - winRect.top);QRect rc = rect();
qDebug () << "paintevent" << rc << qWinRect;painter.fillRect(rc, QColor(0,0,0));
rc.adjust(6, 30, -6, -6);
painter.fillRect(rc, QColor(0, 0, 255, 250));}
bool test2::HandleGetMinMaxInfoMsg( MSG *msg, long *result )
{
QRect maxRect = QApplication::desktop()->availableGeometry();
QSize szMin = this->minimumSize();
QSize szMax = this->maximumSize();
PMINMAXINFO lpMMI = (PMINMAXINFO)msg->lParam;
lpMMI->ptMaxSize.x = maxRect.width();
lpMMI->ptMaxSize.y = maxRect.height();
lpMMI->ptMaxPosition.x = 0;
lpMMI->ptMaxPosition.y = 0;
lpMMI->ptMinTrackSize.x = szMin.width();
lpMMI->ptMinTrackSize.y = szMin.height();
lpMMI->ptMaxTrackSize.x = szMax.width();
lpMMI->ptMaxTrackSize.y = szMax.height();return true;
}
void test2::OnBtnClicked()
{
static bool bMin = false;
bMin = !bMin;if(bMin)
{
showMaximized();
}
else
{
showNormal();
}
}
@I have tried to simplify.
-
Did you take care of this?
bq. Certain window data is cached, so changes you make using SetWindowLong will not take effect until you call the SetWindowPos function. Specifically, if you change any of the frame styles, you must call SetWindowPos with the SWP_FRAMECHANGED flag for the cache to be updated properly.
-
[quote author="MuldeR" date="1399870644"]Did you take care of this?
[/quote]
The SetWindowPos function with the SWP_FRAMECHANGED flag does not take effect. I don't sure whether called properly.
bq.
@SWP_FRAMECHANGED
Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.@Actually I don't change the window's size, just change the background.
So I think the SetWindowPos function should not refer with this problem. -
After SetWindowLong() you need to call SetWindowPos() in order to make the changes take effect. Otherwise you can get "strange" results.
Regardless of the function's name, SetWindowPos() is not just for moving or resizing the window. Read the MSDN article about SetWindowLong() ;-)
@SetWindowLong(hWnd, GWL_STYLE, lStyle | WS_OVERLAPPED | WS_THICKFRAME);
SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);@ -
[quote author="zeljko" date="1399880583"]Then SWP_FRAMECHANGED should be send by SetWindowLong ?[/quote]
[quote author="MuldeR" date="1399897540"]
@SetWindowLong(hWnd, GWL_STYLE, lStyle | WS_OVERLAPPED | WS_THICKFRAME);
SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);@[/quote]I really tried, call SetWindowPos() after SetWindowLong(). It has no effect.
I found a solution to solve this problem, call ::ShowWindow(hWnd, SW_SHOWMAXIMIZED) instead of showMaximized(). But this Widget does exist lots of bugs as call many win32 api.-_-! I can't find a perfect solution. So I have to abandon the System animation and win32 api call.
-
[quote author="zeljko" date="1399957716"]ShowWindow(hWnd, SW_SHOWMAXIMIZED) works but QWidget::showMaximized not ?!? c'mon :) ...
Have you tried (just for test) to hide widget after setting WS_THICKFRAME and then show it again ?[/quote]
Yeah.. You can try the test code(test.h, test.cpp). My Qt version 5.1.1.
I am also very strange! :)