栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

手把手教你写产品级QT项目(二)--- 自定义无边框、阴影,可拖拽、伸缩窗口

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

手把手教你写产品级QT项目(二)--- 自定义无边框、阴影,可拖拽、伸缩窗口

前言

我们今天实现一个无边框,带窗口阴影,可拖拽、伸缩的窗口。

第一步

去除qt窗口的边框、工具栏等属性。我们的窗口的类Window,继承于QWidget这个基础的控件类,然后直接在构造函数内一句函数搞定:

Window::Window(const QSize &initSize, QWidget *parent)
    : QWidget(parent)
{
	// 设置窗口大小
    resize(initSize);
	
	// 去除边框
    setWindowFlags(Qt::Window |Qt::framelessWindowHint);
}

在主程序内直接show这个窗口类:

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    Window *mainWindow = new Window(QSize(994,650);
    mainWindow->show();

    return app.exec();
}

效果如下(桌面背景为白色):

第二步,给它套个阴影
Window::Window(const QSize &initSize, QWidget *parent)
    : QWidget(parent)
{
    resize(initSize);

    setWindowFlags(Qt::Window |Qt::framelessWindowHint);

    HWND hwnd = reinterpret_cast(winId());
    LONG style = static_cast( WS_POPUP | WS_CAPTION | (true ? WS_MINIMIZEBOX : 0) | (true ? WS_MAXIMIZEBOX : 0) | WS_THICKframe | WS_CLIPCHILDREN );
    ::SetWindowLongPtr(hwnd, GWL_STYLE, style);

    const MARGINS shadow = {1, 1, 1, 1};
    DwmExtendframeIntoClientArea(hwnd, &shadow);

}

效果如下:

第三步,加上实现可拖拽、伸缩效果

原理是重写nativeEvent函数,处理windows下的鼠标事件,其实也就是把窗口边界的事件转化为Windows的窗口事件

bool Window::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
	if (eventType != "windows_generic_MSG")
		return false;

	MSG* msg = reinterpret_cast(message);
	QWidget* widget = QWidget::find(reinterpret_cast(msg->hwnd));
	if (!widget)
		return false;

	switch (msg->message)
    {
	case WM_NCCALCSIZE:
	{
		*result = 0;
		return true;
	}
	case WM_NCHITTEST:
	{
		const LONG borderWidth = 9;
		RECT winrect;
		::GetWindowRect(msg->hwnd, &winrect);
		long x = GET_X_LPARAM(msg->lParam);
		long y = GET_Y_LPARAM(msg->lParam);

		if (m_resizeable && m_freeSizeable)
		{
			// bottom left
			if (x >= winrect.left && x < winrect.left + borderWidth &&
					y < winrect.bottom && y >= winrect.bottom - borderWidth)
			{
				*result = HTBOTTOMLEFT;
				return true;
			}

			// bottom right
			if (x < winrect.right && x >= winrect.right - borderWidth &&
					y < winrect.bottom && y >= winrect.bottom - borderWidth)
			{
				*result = HTBOTTOMRIGHT;
				return true;
			}

			// top left
			if (x >= winrect.left && x < winrect.left + borderWidth &&
					y >= winrect.top && y < winrect.top + borderWidth)
			{
				*result = HTTOPLEFT;
				return true;
			}

			// top right
			if (x < winrect.right && x >= winrect.right - borderWidth &&
					y >= winrect.top && y < winrect.top + borderWidth)
			{
				*result = HTTOPRIGHT;
				return true;
			}

			// left
			if (x >= winrect.left && x < winrect.left + borderWidth)
			{
				*result = HTLEFT;
				return true;
			}

			// right
			if (x < winrect.right && x >= winrect.right - borderWidth)
			{
				*result = HTRIGHT;
				return true;
			}

			// bottom
			if (y < winrect.bottom && y >= winrect.bottom - borderWidth)
			{
				*result = HTBOTTOM;
				return true;
			}

			// top
			if (y >= winrect.top && y < winrect.top + borderWidth)
			{
				*result = HTTOP;
				return true;
			}
		}


		// 高分屏
		double dpr = this->devicePixelRatioF();
		QPoint pos = mapFromGlobal(QPoint(static_cast(x/dpr),
										  static_cast(y/dpr)));

		// 全区域移动,前提是窗口内没有子控件,有的话需要加入m_ignoreList
		if (m_fullAreaMoveable)
		{
			if (!rect().contains(pos)) return false;

			QWidget *curParent = this;
			while (1)
			{
				QWidget* child = curParent->childAt(pos);
				if (!child)
				{
					*result = HTCAPTION;
					return true;
				}
				else
				{
					// 当鼠标在m_ignoreList列表的窗口子控件内时才能拖动窗口
					if (m_ignoreList.contains(child))
					{
						curParent = child;
						continue;
					}
					else
						break;
				}
			}

			break;
		}


		// 标题栏移动
		if (!m_titleBar) return false;

		if (!m_titleBar->rect().contains(pos)) return false;
		QWidget* child = m_titleBar->childAt(pos);
		if (!child)
		{
			*result = HTCAPTION;
			return true;
		}

		return false;
	}

	case WM_GETMINMAXINFO:
	{
		// 最大化窗口部件位置修正
		if (::IsZoomed(msg->hwnd))
		{

			RECT frame = { 0, 0, 0, 0 };
			AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);
			frame.left = abs(frame.left);
			frame.top = abs(frame.bottom);
			widget->setContentsMargins(frame.left, frame.top, frame.right, frame.bottom);
		}
		else
		{
			widget->setContentsMargins(0, 0, 0, 0);
		}

		// 限定最小及最大窗口
		reinterpret_cast(msg->lParam)->ptMinTrackSize.x = minimumWidth();
		reinterpret_cast(msg->lParam)->ptMinTrackSize.y = minimumHeight();
		reinterpret_cast(msg->lParam)->ptMaxTrackSize.x = maximumWidth();
		reinterpret_cast(msg->lParam)->ptMaxTrackSize.y = maximumHeight();

		*result = ::DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
		return true;
	}
	default:
		break;
	}

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

// 加入全局拖拽时的控件忽略列表
void Window::AddToMoveableIgnoreList(QWidget *widget)
{
	m_ignoreList.append(widget);
}

// 使能全局拖拽
void Window::SetFullAreaMoveable(bool enable)
{
	m_fullAreaMoveable = enable;
}

这样调用窗口

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    Window *mainWindow = new Window(QSize(994, 650));

    mainWindow->SetFullAreaMoveable(true);
    mainWindow->SetResizeable(true);

    mainWindow->show();

    return app.exec();
}

效果如下:

如有疑问欢迎交流
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/743431.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号