Qt quick实现无边框可拖拽风格
2016-06-21 10:32
811 查看
由来:
现在大量的软件采用简洁的风格,即移除系统的标题栏及边框并自己实现标题栏以达到定制的目的,由于我们写要用Qt写一个类似IM的软件,故有此需求。方法:
去除系统边框比较简单,setFlags(Qt::Window | Qt::FramelessWindowHint);即可,但下一步要实现窗体的拖拽和缩放。1,最简单的方法:
这种方法比较简单直观,即点击鼠标的时候记录下鼠标位置,然后在鼠标移动的时候对窗体进行移动。示例代码如下:MouseArea { //为窗口添加鼠标事件 property int xMouse //存储鼠标x坐标 property int yMouse //存储鼠标y坐标 anchors.fill: parent acceptedButtons: Qt.LeftButton //只处理鼠标左键 drag.filterChildren: true onPressed: { //接收鼠标按下事件 xMouse = mouse.x yMouse = mouse.y } onPositionChanged: { //鼠标按下后改变位置 loginScreen.x = loginScreen.x + (mouse.x - xMouse) loginScreen.y = loginScreen.y + (mouse.y - yMouse) } }
以上方法简单直观,但当窗体比较复杂的时候将发生窗体移动比较迟钝,甚至,当从上标题快速拖拽窗体时,鼠标移出窗体,而窗体不再移动的问题。
网上的一个案例,大家可以参考http://blog.sina.com.cn/s/blog_a6fb6cc90101au8r.html
2,改进方案:
观察系统默认的带标题栏的窗体,发现,拖动窗体时,窗体并不移动,而是由一个矩形框随着鼠标移动,当松开鼠标后窗体才进行一次性地移动。于是,有了一个可行的方案(因为后面有更优的方案,但是如果后面的方案不行,这个方案可能成为最后的方案),当拖拽的时候绘制一个移动的和原窗体等大小的矩形进行移动,最后再一次性地移动(相信系统的拖拽也是采用类似的方案)。具体实施略,可以自行测试。
3,继续改进方案,应该是一个较优方案:
具体原理我也是参照这个博客http://blog.sina.com.cn/s/blog_6288219501015dwa.html,讲得比较详细,虽然是mfc程序,但毕竟也是windows程序,原理相同。摘抄一段:
当窗口确定鼠标位置时,Windows向窗口发送WM_NCHITTEST消息,可以处理该消息,使得只要鼠标在窗口内,Windows便认为鼠标在标题条上。这需要重载CWnd类处理WM_NCHITTEST消息的OnNcHitTest函数,在函数中调用父类的该函数,如果返回HTCLIENT,说明鼠标在窗口客户区内,使重载函数返回HTCAPTION,使Windows误认为鼠标处于标题条上。
UINT MyWndDlg::OnNcHitTest(CPoint point) { // 取得鼠标所在的窗口区域 UINT nHitTest = CDialog::OnNcHitTest(point); // 如果鼠标在窗口客户区,则返回标题条代号给Windows // 使Windows按鼠标在标题条上类进行处理,即可单击移动窗口 return (nHitTest==HTCLIENT) ? HTCAPTION : nHitTest; }
知道了原理咱便可以开工了!!
接着又发现了如下例子:http://blog.csdn.net/kfbyj/article/details/9284923,不过貌似博主文章是在Qt4时写的,现在早就没有MainWindow::winEvent(MSG *message, long *result)函数了呀!!
不过既然Qt堵了一扇门,他一定会打开另一扇窗,于是有了nativeEvent函数~~
参看该博客http://blog.csdn.net/tujiaw/article/details/43836039,哈哈,貌似一切都搞定了~ 确实,我们可以开始写自己的无边框窗体了~~
上代码:
#include "framelessWindow.h" #include <QQuickItem> FramelessWindow::FramelessWindow(QWindow *parent) : QQuickWindow(parent) { setFlags(Qt::Window | Qt::FramelessWindowHint); } FramelessWindow::~FramelessWindow() { } void FramelessWindow::setTitleBar(QQuickItem *titleBar) { m_pTitleBar = titleBar; } bool FramelessWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) { Q_UNUSED(eventType); if ( windowState() && Qt::WindowMaximized) { return false; } const int HIT_BORDER = 8; const MSG *msg=static_cast<MSG*>(message); if(msg->message == WM_NCHITTEST) { int xPos = ((int)(short)LOWORD(msg->lParam)) - this->frameGeometry().x(); int yPos = ((int)(short)HIWORD(msg->lParam)) - this->frameGeometry().y(); if (m_pTitleBar && m_pTitleBar->contains(QPointF(xPos,yPos))) { *result = HTCAPTION; } if (m_pTitleBar) { if (m_pTitleBar->contains(QPointF(xPos,yPos)) && !m_pTitleBar->childAt(xPos,yPos)) { *result = HTCAPTION; return true; } } else { /* if(contentItem()->childItems()[0]->childAt(xPos,yPos) == 0) { *result = HTCAPTION; }*/ } auto child = contentItem()->childAt(xPos,yPos); if(child) { if (child != m_pTitleBar) { return false; } else { if (m_pTitleBar && !m_pTitleBar->childAt(xPos,yPos)) { *result = HTCAPTION; return true; } return false; } } if(xPos > 0 && xPos < HIT_BORDER) { *result = HTLEFT; } if(xPos > (this->width() - HIT_BORDER) && xPos < (this->width() - 0)) { *result = HTRIGHT; } if(yPos > 0 && yPos < HIT_BORDER) { *result = HTTOP; } if(yPos > (this->height() - HIT_BORDER) && yPos < (this->height() - 0)) { *result = HTBOTTOM; } if(xPos > 0 && xPos < HIT_BORDER && yPos > 0 && yPos < HIT_BORDER) { *result = HTTOPLEFT; } if(xPos > (this->width() - HIT_BORDER) && xPos < (this->width() - 0) && yPos > 0 && yPos < HIT_BORDER) { *result = HTTOPRIGHT; } if(xPos > 0 && xPos < HIT_BORDER && yPos > (this->height() - HIT_BORDER) && yPos < (this->height() - 0)) { *result = HTBOTTOMLEFT; } if(xPos > (this->width() - HIT_BORDER) && xPos < (this->width() - 0) && yPos > (this->height() - HIT_BORDER) && yPos < (this->height() - 0)) { *result = HTBOTTOMRIGHT; } return true; } return false; }
大体和前人的代码类似,略有些小改动,下面会说这些改动的原因。
4,进一步,将c++类导出到qml中使用:
其实下一步相对来说比较简单,套路即可:在main函数中加入如下代码:qmlRegisterType(“com.FramelessWindow”, 1, 0, “FramelessWindow”);
话说要记得FramelessWindow得继承自QObject才能导出哦,还有,我们的类得是用在主窗体下,所以继承自QQuickWindow
#ifndef MYWINDOW_H #define MYWINDOW_H #include <QQuickWindow> class FramelessWindow : public QQuickWindow { Q_OBJECT public: explicit FramelessWindow(QWindow *parent = 0); ~FramelessWindow(); Q_INVOKABLE void setTitleBar(QQuickItem *titleBar); signals: public slots: // QWidget interface protected: bool nativeEvent(const QByteArray &eventType, void *message, long *result); private: QQuickItem *m_pTitleBar; }; #endif // MYWINDOW_H
于是一切搞定,最后附上具体使用的qml
import QtQuick.Window 2.2 import QtWebEngine 1.2 import QtQuick 2.5 import QtQuick.Controls 1.4 import QtWebChannel 1.0 import com.FramelessWindow 1.0 FramelessWindow { width: 500 height: 859 id:window TitleBar { id: titleBar anchors.top: parent.top anchors.left: parent.left width: parent.width height: 100 color: "blue" } Component.onCompleted: { window.setTitleBar(titleBar) } }
看,我们指定了一个TitleBar(其实就是一个Rectangle),注意setTitleBar一定得是Q_INVOKABLE或者定义为slot,否则qml中无法调用!
还有这段
if (m_pTitleBar && m_pTitleBar->contains(QPointF(xPos,yPos))) { *result = HTCAPTION; } if (m_pTitleBar) { if (m_pTitleBar->contains(QPointF(xPos,yPos)) && !m_pTitleBar->childAt(xPos,yPos)) { *result = HTCAPTION; return true; } } else { /* if(contentItem()->childItems()[0]->childAt(xPos,yPos) == 0) { *result = HTCAPTION; }*/ }
用于判断是否设置了titlebar,而且点击位置十分在控件下,比较简单~~
相关文章推荐
- easyui的自定义行工具栏
- UI控件之RadioButton(单选按钮)&Checkbox(复选按钮)
- iOS 设置UITextView显示文本的光标
- UItableViewCell的分割线顶头显示
- NGUI 拓展Poplist 插件
- ios UISegmentedControl的使用简介
- UIImagePickerController简单使用
- UUID的意义和作用
- private static final long serialVersionUID = 1L
- UIView中的坐标转换
- UIButton的详细使用
- iOS实战演练之Unknown type name 'UIColor" 的问题
- UIViewController的edgesForExtendedLayout属性
- iOS中长按调出菜单组件UIMenuController的使用实例
- [置顶] Java并发编程与技术内幕:ArrayBlockingQueue、LinkedBlockingQueue及SynchronousQueue源码解析
- SwipeRefreshLayout在onCreate使用setRefreshing(true)无效
- 描述Handler,Looper,MessageQueue,Message之间的关系
- Instant Run requires 'Tools | Android | Enable ADB integration' to be enabled.
- java后台获取 form提交的name和value
- Assertion failure in -[UITableView _configureCellForDisplay:forIndexPath:]