Qt 窗口陰影

Qt實現窗口陰影網上有多種實現方式,比如:

1.使用qt自帶的方法,QGraphicsDropShadowEffect

2.通過在無邊框背景透明的窗口中paintevent事件中繪製出來

3.通過dwmapi庫的陰影功能具體代碼如下:

#pragma once
#include <QWidget>
#include "ui_customwidgetframe.h"

class CustomWidgetFrame : public QWidget {
	Q_OBJECT

public:
	CustomWidgetFrame(QWidget * parent = Q_NULLPTR);
	~CustomWidgetFrame();

	bool nativeEvent(const QByteArray &eventType, void *message, long *result);

private:
	Ui::CustomWidgetFrame ui;
};
#include "customwidgetframe.h"
#include <dwmapi.h>
#include <windowsx.h>
#include <qt_windows.h>
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "user32.lib")

CustomWidgetFrame::CustomWidgetFrame(QWidget * parent) : QWidget(parent) {
	ui.setupUi(this);

	setWindowFlags(windowFlags() | Qt::FramelessWindowHint);

	bool visible = isVisible();

	/*
	此行代碼可以帶回Aero效果,同時也帶回了標題欄和邊框,在nativeEvent()會再次去掉標題欄
	主要的關鍵是WS_THICKFRAME,可以實現窗口停靠邊緣自動改變大小功能和Aero效果
	*/
	//this line will get titlebar/thick frame/Aero back, which is exactly what we want
	//we will get rid of titlebar and thick frame again in nativeEvent() later
	HWND hwnd = (HWND)this->winId();
	DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
	::SetWindowLong(hwnd, GWL_STYLE, style | WS_THICKFRAME);

	//保留一個像素的邊框寬度,否則系統不會繪製邊框陰影
	//
	//we better left 1 piexl width of border untouch, so OS can draw nice shadow around it
	const MARGINS shadow = { 1, 1, 1, 1 };
	DwmExtendFrameIntoClientArea(HWND(winId()), &shadow);

}

CustomWidgetFrame::~CustomWidgetFrame() {
	
}

bool CustomWidgetFrame::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
	MSG *param = static_cast<MSG *>(message);
	static long cnt = 10000;
	printf("######:%x --- %d\n", param->message, ++cnt);
	switch (param->message)
	{
	case WM_NCCALCSIZE: {
		/*
		此消息用於處理非客戶區域,比如邊框的繪製
		返回false,就是按系統的默認處理,如果返回true,而不做任何繪製,則非客戶區域
		就不會被繪製,就相當於沒有繪製非客戶區域,所以就會看不到非客戶區域的效果
		*/
		return true;
	}
	case WM_NCHITTEST:
	{
		const LONG border_width = 3;
		RECT winrect;
		GetWindowRect(HWND(winId()), &winrect);

		long x = GET_X_LPARAM(param->lParam);
		long y = GET_Y_LPARAM(param->lParam);

        /*
        只用這種辦法設置動態改變窗口大小比手動通過鼠標事件效果好,可以
        避免閃爍問題
        */
		//left border
		if (x >= winrect.left && x < winrect.left + border_width)
		{
			*result = HTLEFT;
			return true;
		}
		//right border
		if (x < winrect.right && x >= winrect.right - border_width)
		{
			*result = HTRIGHT;
			return true;
		}

		//bottom border
		if (y < winrect.bottom && y >= winrect.bottom - border_width)
		{
			*result = HTBOTTOM;
			return true;
		}
		//top border
		if (y >= winrect.top && y < winrect.top + border_width)
		{
			*result = HTTOP;
			return true;
		}
		//bottom left corner
		if (x >= winrect.left && x < winrect.left + border_width &&
			y < winrect.bottom && y >= winrect.bottom - border_width)
		{
			*result = HTBOTTOMLEFT;
			return true;
		}
		//bottom right corner
		if (x < winrect.right && x >= winrect.right - border_width &&
			y < winrect.bottom && y >= winrect.bottom - border_width)
		{
			*result = HTBOTTOMRIGHT;
			return true;
		}
		//top left corner
		if (x >= winrect.left && x < winrect.left + border_width &&
			y >= winrect.top && y < winrect.top + border_width)
		{
			*result = HTTOPLEFT;
			return true;
		}
		//top right corner
		if (x < winrect.right && x >= winrect.right - border_width &&
			y >= winrect.top && y < winrect.top + border_width)
		{
			*result = HTTOPRIGHT;
			return true;
		}


		return QWidget::nativeEvent(eventType, message, result);
	}
	}

	return QWidget::nativeEvent(eventType, message, result);
}

第3種方法效果還是不錯的,比較推薦使用,效果和系統效果很像

該代碼有2個主要功能:

1.陰影功能

2.拉動窗口改變大小功能,這裏的拉動窗口改變大小功能比較推薦使用,可以避免閃爍問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章