一、前言
实现的效果为:通过黑色矩形框预操作,鼠标释放时更新窗口。效果图如下:
1.功能
1.1 已实现功能
- 8个方向的缩放
- 标题栏拖动
- 标题栏双击最大化/正常窗口
- 窗口最小尺寸
- 预操作框颜色与背景色互补
- 多屏幕
- 默认标题栏
1.2 待开发功能
- 拖动到屏幕四周进行半屏全屏。
1.3 存在的问题
1.QWebEngineView
从测试效果来看,对于普通widget效果较好,但是如果是嵌入了QWebEngineView,且其位于窗口四周,就会出现问题。
其主要原因在于,当事件进入QWebEngineView后,会被其child(QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget)"吃掉",导致后续事件不再进入主窗口,从而无法触发我们的代码。这时我们还可以遍历QWebEngineView的children,将事件安装到主窗口
auto childs = view->children();
for(auto child:childs){
child->installEventFilter(this);
}
但是这样显得太笨重了。而且,即使如此,鼠标样式的修改,也会被QWebEngineView刷掉。因此对于QWebEngineView下是存在一些问题的。
2.多屏幕拖动时,从低缩放比拖到高缩放比释放时,会闪烁一次,目前没有进一步分析原因。
2.探索的过程
2.1 关于无边框缩放的实现方式
目前网上无非两种方式:
a)通过nativeEvent()实现。c++ - Qt/Windows, resizable frameless window - Stack Overflow
b)通过窗口的eventFilter()直接实现。
但是问题是前者基于windows接口不跨平台,而后者则会出现抖动。
本文则是基于第二种方式,并参考windows窗口的拖动方式,通过预操作框避免抖动的问题。
2.2 实现思路
-
首先关于无边框窗口,通过Qt标志处理:
setWindowFlags(Qt::FramelessWindowHint);
-
然后关于缩放和拖动,与网上大多数实现一致,通过九宫格法实现:
当鼠标位于不同区域时,触发不同的操作。
- 而对于预处理框,则是通过一个覆盖整个屏幕的透明窗口,并重写paintEvent在其中绘制一个rect。
2.3 实现难点
1.首先是九宫格的思路,了解了思路之后,实现就比较简单了
2.对于预处理框,首先是需要以全屏幕作为geometry,否则会导致拖不到非主屏幕,或者位置错误灯问题
3.其次是预处理框要实现window类似的与背景色互补(而非纯色),需要获取全屏幕图片作为背景绘制到预操作框,然后再通过QPainter::RasterOp_SourceAndNotDestination模式取反。
4.最后就是对于多屏幕的位置,缩放比灯。
二、代码
目录结构如下:
其中:
- cursorTransform是用于鼠标位置和方向的计算
- framelessData则是一些全局变量
- framelessHelper则是对外的接口,
- titleBar表示默认标题栏
- transparentRect则是实现预操作框
详细代码如下:
#include "cursorTransform.h"
#include <QDebug>
AreaType CursorTransform::calculateCursorType(const QSize &size, const QPoint &pos)
{
//TODO:minsize
auto w = size.width();
auto h = size.height();
auto x = pos.x();
auto y = pos.y();
//left
if(x<=FrameLessData::borderThickness){
if(y<=FrameLessData::borderThickness)
return AreaType::LeftTopArea;
else if(y>=h-FrameLessData::borderThickness)
return AreaType::LeftBottomArea;
else
return AreaType::LeftArea;
}//right
else if(x>=w-FrameLessData::borderThickness){
if(y<=FrameLessData::borderThickness)
return AreaType::RightTopArea;
else if(y>=h-FrameLessData::borderThickness)
return AreaType::RightBottomArea;
else
return AreaType::RightArea;
}
else{//center
if(y<=FrameLessData::borderThickness)
return AreaType::TopArea;
else if(y<=FrameLessData::titleHeight)
return AreaType::TitleArea;
else if(y>=h-FrameLessData::borderThickness)
return AreaType::BottomArea;
else
return AreaType::CenterArea;
}
}
Qt::CursorShape CursorTransform::AreaType2CursorShape(AreaType type)
{
Qt::CursorShape cursorType(Qt::ArrowCursor);
switch (type) {
case AreaType::LeftTopArea:
case AreaType::RightBottomArea:
cursorType=Qt::SizeFDiagCursor;
break;
case AreaType::RightTopArea:
case AreaType::LeftBottomArea:
cursorType=Qt::SizeBDiagCursor;
break;
case AreaType::TopArea:
case AreaType::BottomArea:
cursorType=Qt::SizeVerCursor;
break;
case AreaType::LeftArea:
case AreaType::RightArea:
cursorType=Qt::SizeHorCursor;
break;
default:
break;
}
return cursorType;
}
void CursorTransform::calculateRect(AreaType type,QRect &rect, const QPoint &lastPos,const QPoint &newPos)
{
int xOff=0,yOff=0,wOff=0,hOff=0;
int posXOff=newPos.x()-lastPos.x();
int posYOff = newPos.y()-lastPos.y();
switch (type) {
case AreaType::LeftTopArea:
xOff = posXOff;
yOff = posYOff;
break;
case AreaType::TopArea:
yOff = posYOff;
break;
case AreaType::RightTopArea:
yOff = posYOff;
wOff = posXOff;
break;
case AreaType::LeftArea:
xOff = posXOff;
break;
case AreaType::RightArea:
wOff = posXOff;
break;
case AreaType::LeftBottomArea:
xOff = posXOff;
hOff = posYOff;
break;
case AreaType::BottomArea:
hOff = posYOff;
break;
case AreaType::RightBottomArea:
wOff = posXOff;
hOff = posYOff;
break;
case AreaType::TitleArea: //move
rect.moveTopLeft(rect.topLeft()+QPoint(posXOff,posYOff));
break;
default:
break;
}
//min judget:
if(rect.width()-xOff<=FrameLessData::minWidth)
xOff=0;
else if(rect.left()<newPos.x() && xOff<0)
xOff=0;
if(rect.width()+wOff<=FrameLessData::minWidth)
wOff=0;
else if(rect.right()>newPos.x() && wOff>0)
wOff=0;
if(rect.height()-yOff<=FrameLessData::minHeight)
yOff=0;
else if(rect.top()<newPos.y() && yOff<0)
yOff=0;
if(rect.height()+hOff<=FrameLessData::minHeight)
hOff=0;
else if(rect.bottom()>newPos.y() && hOff>0)
hOff=0;
//resize:
rect.setTop(rect.top()+yOff);
rect.setLeft(rect.left()+xOff);
rect.setWidth(rect.width()+wOff);
rect.setHeight(rect.height()+hOff);
}
#ifndef CURSORTRANSFORM_H
#define CURSORTRANSFORM_H
#include "framelessData.h"
#include <QRect>
class CursorTransform
{
public:
static AreaType calculateCursorType(const QSize &size, const QPoint &pos);
static enum Qt::CursorShape AreaType2CursorShape(AreaType type);
static void calculateRect(AreaType type,QRect &rect,const QPoint &lastPos,const QPoint &newPos);
private:
};
#endif // CURSORTRANSFORM_H
#include "framelessData.h"
int FrameLessData::borderThickness=4;
int FrameLessData::titleHeight=30;
int FrameLessData::minHeight=355;
int FrameLessData::minWidth=220;
QColor FrameLessData::borderColor=(Qt::white);
QColor FrameLessData::transparentRectBkground=(Qt::transparent);
enum Qt::PenStyle FrameLessData::borderStyle=(Qt::SolidLine);
#ifndef FRAMELESSDATA_H
#define FRAMELESSDATA_H
#include <QColor>
#include <QPoint>
#include <QSize>
//透明矩形的属性
class FrameLessData{
public:
static int borderThickness;
static int titleHeight;
static int minHeight;
static int minWidth;
static QColor borderColor;
static QColor transparentRectBkground;
static enum Qt::PenStyle borderStyle;
};
enum class AreaType
{
LeftTopArea,
TopArea,
RightTopArea,
LeftArea,
RightArea,
LeftBottomArea,
BottomArea,
RightBottomArea,
CenterArea,
TitleArea,
None
};
enum class WidgetOperator
{
None,
Resize,
Move
};
#endif // FRAMELESSDATA_H
#include "framelessHelper.h"
#include "cursorTransform.h"
#include <QEvent>
#include <QSize>
#include <QPoint>
#include <QCursor>
#include <QWidget>
#include <QPainter>
#include <QDebug>
FrameLessHelper::FrameLessHelper(QWidget *widget)
: m_widget(widget)
, m_transparentRect(new TransparentRect())
, m_widgetOperator(WidgetOperator::None)
, m_isMove(false)
#ifdef TITLEBAR
, m_titleBar(nullptr)
#endif
{
m_transparentRect->setRect(m_widget->frameGeometry());
#ifdef TITLEBAR
//if use title
m_titleBar = new TitleBar(m_widget);
connect(m_titleBar,&TitleBar::minimumSizeClick,this,&FrameLessHelper::onMinClick);
connect(m_titleBar,&TitleBar::showNormalClick,this,&FrameLessHelper::onMaxClick);
connect(m_titleBar,&TitleBar::closeClick,this,&FrameLessHelper::onCloseClick);
#endif
}
bool FrameLessHelper::eventFilter(QObject *watched, QEvent *event)
{
int ret=false;
switch (event->type()) {
case QEvent::MouseButtonDblClick: //double click
ret = onDbClickEvent(static_cast<QMouseEvent *>(event));
break;
case QEvent::MouseButtonPress:
ret = onMousePressEvent(static_cast<QMouseEvent *>(event));
break;
case QEvent::MouseButtonRelease:
ret = onMouseReleaseEvent(static_cast<QMouseEvent *>(event));
break;
case QEvent::MouseMove:
ret = onMoveEvent(static_cast<QMouseEvent *>(event));
break;
case QEvent::HoverMove:
case QEvent::HoverEnter:
ret = onHoverEvent(watched,static_cast<QHoverEvent *>(event));
break;
default:
break;
}
//触发返回true
if(ret)
return ret;
return QObject::eventFilter(watched,event);
}
void FrameLessHelper::setBorderThickness(int thickness)
{
FrameLessData::borderThickness = thickness;
}
void FrameLessHelper::setTitleHeight(int height)
{
FrameLessData::titleHeight = height;
}
void FrameLessHelper::setMinHeight(int height)
{
FrameLessData::minHeight = height;
}
void FrameLessHelper::setMinWidth(int width)
{
FrameLessData::minWidth = width;
}
#ifdef TITLEBAR
QWidget *FrameLessHelper::getTitleBar()
{
return m_titleBar;
}
void FrameLessHelper::onMinClick()
{
m_widget->showMinimized();
}
void FrameLessHelper::onMaxClick()
{
if(m_widget->isFullScreen())
m_widget->showNormal();
else
m_widget->showFullScreen();
}
void FrameLessHelper::onCloseClick()
{
m_widget->close();
}
#endif
bool FrameLessHelper::onHoverEvent(QObject *watched,QHoverEvent *event)
{
//操作的时候不响应hover
if(m_widgetOperator!=WidgetOperator::None || m_widget->isFullScreen())
return false;
//hover move
auto areatype = CursorTransform::calculateCursorType(m_widget->size(),event->pos());
auto w=static_cast<QWidget *>(watched);
if(w)
w->setCursor(CursorTransform::AreaType2CursorShape(areatype));
else
m_widget->setCursor(CursorTransform::AreaType2CursorShape(areatype));
return true;
//hover leave -> Qt::ArrowCursor
//not need
}
bool FrameLessHelper::onDbClickEvent(QMouseEvent *event)
{
if(event->button()!=Qt::LeftButton){
return false;
}
auto areatype = CursorTransform::calculateCursorType(m_widget->size(),event->globalPos()-m_widget->pos());
if(areatype==AreaType::TitleArea){
if(m_widget->isFullScreen()){
m_widget->showNormal();
}
else
{
m_widget->showFullScreen();
}
#ifdef TITLEBAR
m_titleBar->onMaxClick();
#endif
}
return true;
}
bool FrameLessHelper::onMousePressEvent(QMouseEvent *event)
{
if(event->button()!=Qt::LeftButton){
return false;
}
auto type = CursorTransform::calculateCursorType(m_widget->size(),event->globalPos()-m_widget->pos());
if(type<AreaType::CenterArea){
//resize
m_widgetOperator = WidgetOperator::Resize;
// not start show rect
m_transparentRect->setRect(m_widget->frameGeometry());
m_transparentRect->onMousePressEvent(event,type);
return true;
}
else if(type==AreaType::TitleArea){
//move
m_widgetOperator = WidgetOperator::Move;
if(!m_widget->isFullScreen())
m_transparentRect->setRect(m_widget->geometry());
else{
m_transparentRect->setRect(calMaxMoveRect(event->globalPos()));
}
m_transparentRect->onMousePressEvent(event,type);
return true;
}
return false;
}
bool FrameLessHelper::onMoveEvent(QMouseEvent *event)
{
if(m_widgetOperator==WidgetOperator::None ||
(m_widgetOperator == WidgetOperator::Resize && m_widget->isFullScreen())){
return false;
}
#ifdef TITLEBAR
if(m_widget->isFullScreen())
m_titleBar->onMaxClick();
#endif
m_isMove =true;
m_transparentRect->show();
m_transparentRect->onMoveEvent(event,event->globalPos()-m_widget->pos());
return false;
}
bool FrameLessHelper::onMouseReleaseEvent(QMouseEvent *event)
{
if(event->button()!=Qt::LeftButton || m_widgetOperator==WidgetOperator::None){
return false;
}
m_transparentRect->onMouseReleaseEvent(event);
m_transparentRect->hide();
if(m_isMove){
if(m_widget->isFullScreen())
m_widget->showNormal();
auto &&rect = m_transparentRect->getRect();
m_widget->move(rect.topLeft());
m_widget->resize(rect.size());
}
m_isMove = false;
m_widgetOperator = WidgetOperator::None;
return false;
}
QRect FrameLessHelper::calMaxMoveRect(const QPoint &evPos)
{
//最大化时拖动标题栏的规则:宽度分为3份,鼠标位于左侧则向左上角对齐,右侧则向右上角对齐,中间部分则按中间对齐
auto rect = m_widget->normalGeometry();
rect.moveTop(0);
double gPosX=evPos.x();
double step = m_widget->geometry().width()/3.0;
double left=0;
if(gPosX>step*2){
left=step*3-rect.width();
}
else if(gPosX>step && gPosX<=step*2){
left=gPosX-rect.width()/2.0;
}
rect.moveLeft(left);
return rect;
}
#ifndef FRAMELESSHELPER_H
#define FRAMELESSHELPER_H
#include "transparentRect.h"
#include "titleBar.h"
#include "cursorTransform.h"
#include <QObject>
#include <QMouseEvent>
#include <QHoverEvent>
#include <QRect>
#include <QWidget>
/*
FrameLessHelper
1.管理窗口无边框功能
2.输入为QWidget(构造)和QEvent(eventFilter)
*/
class FrameLessHelper : public QObject
{
Q_OBJECT
public:
FrameLessHelper(QWidget *widget);
bool eventFilter(QObject *watched, QEvent *event) override;
void setBorderThickness(int thickness);
void setTitleHeight(int height);
void setMinHeight(int height);
void setMinWidth(int width);
#ifdef TITLEBAR
QWidget *getTitleBar();
void onMinClick();
void onMaxClick();
void onCloseClick();
#endif
private:
bool onHoverEvent(QObject *watched,QHoverEvent *event);
bool onDbClickEvent(QMouseEvent *event);
bool onMousePressEvent(QMouseEvent *event);
bool onMoveEvent(QMouseEvent *event);
bool onMouseReleaseEvent(QMouseEvent *event);
QRect calMaxMoveRect(const QPoint &evPos);
//代理窗口
QWidget *m_widget;
//预拖动窗口
TransparentRect *m_transparentRect;
enum WidgetOperator m_widgetOperator;
bool m_isMove;
#ifdef TITLEBAR
TitleBar *m_titleBar;
#endif
};
#endif // FRAMELESSHELPER_H
#include "titleBar.h"
#include <QIcon>
#include <QPalette>
int iconSize=30;
TitleBar::TitleBar(QWidget *parent,bool isMax)
: QWidget{parent}
, m_minBtn{new QPushButton(this)}
, m_maxBtn{new QPushButton(this)}
, m_closeBtn{new QPushButton(this)}
, m_hLayout{new QHBoxLayout(this)}
, m_spacer{new QSpacerItem(500,30,QSizePolicy::Expanding)}
, m_isMax{isMax}
{
m_minBtn->setIcon(QIcon(":/img/min.png"));
QString maxPic = m_isMax?":/img/normal.png":":/img/max.png";
m_maxBtn->setIcon(QIcon(maxPic));
m_closeBtn->setIcon(QIcon(":/img/close.png"));
connect(m_minBtn,&QPushButton::clicked,this,&TitleBar::minimumSizeClick);
connect(m_maxBtn,&QPushButton::clicked,this,&TitleBar::showNormalClick);
connect(m_maxBtn,&QPushButton::clicked,this,&TitleBar::onMaxClick);
connect(m_closeBtn,&QPushButton::clicked,this,&TitleBar::closeClick);
m_minBtn->resize(iconSize,iconSize);
m_maxBtn->resize(iconSize,iconSize);
m_maxBtn->resize(iconSize,iconSize);
m_hLayout->addSpacerItem(m_spacer);
m_hLayout->addWidget(m_minBtn);
m_hLayout->addWidget(m_maxBtn);
m_hLayout->addWidget(m_closeBtn);
m_hLayout->setSpacing(0);
m_hLayout->setContentsMargins(0,0,0,0);
QPalette palette(this->palette());
palette.setColor(QPalette::Background, QColor(100,100,100));
this->setAutoFillBackground(true);
this->setPalette(palette);
}
void TitleBar::onMaxClick()
{
if(m_isMax){
m_maxBtn->setIcon(QIcon(":/img/max.png"));
m_isMax=false;
}
else{
m_maxBtn->setIcon(QIcon(":/img/normal.png"));
m_isMax=true;
}
}
#ifndef TITLEBAR_H
#define TITLEBAR_H
#include <QWidget>
#include <QPushButton>
#include <QHBoxLayout>
#include <QSpacerItem>
class TitleBar : public QWidget
{
Q_OBJECT
public:
explicit TitleBar(QWidget *parent = nullptr,bool isMax=false);
void onMaxClick();
signals:
void minimumSizeClick();
void showNormalClick();
void closeClick();
private:
QPushButton *m_minBtn;
QPushButton *m_maxBtn;
QPushButton *m_closeBtn;
QHBoxLayout *m_hLayout;
QSpacerItem *m_spacer;
bool m_isMax;
};
#endif // TITLEBAR_H
#include "transparentRect.h"
#include "framelessData.h"
#include <QMouseEvent>
#include <QDesktopWidget>
#include <QDebug>
#include <QPainter>
#include <QPen>
#include <QScreen>
#include <QGuiApplication>
#include <QList>
TransparentRect::TransparentRect(QWidget *parent)
: QWidget(parent)
, m_areaType(AreaType::None)
{
setWindowFlag(Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
}
void TransparentRect::setRect(const QRect &rect)
{
m_rect=rect;
}
QRect TransparentRect::getRect() const
{
return m_rect;
}
void TransparentRect::onMousePressEvent(QMouseEvent *event,AreaType type)
{
m_lastPos = event->pos();
//
auto screens = QGuiApplication::screens();
QRect rect;
//QPainter painter(&m_pixmap);
for(auto s:screens){
auto geom = s->geometry();
rect |= geom;
//auto pixmap = s->grabWindow(0, geom.x(), geom.y(), geom.width(), geom.height());
//static int i = 0;
//QFile file(QString("./test%1.png").arg(i++));
//file.open(QIODevice::WriteOnly);
//pixmap.save(&file);
//file.close();
//painter.drawPixmap(s->geometry(), pixmap);
}
//painter.end();
m_startInch = QGuiApplication::screenAt(event->globalPos())->logicalDotsPerInch();
setGeometry(rect);
m_pixmap= QGuiApplication::primaryScreen()->grabWindow(0, rect.x(), rect.y(), rect.width(), rect.height());
m_areaType = type;
#if 0
//
QFile file("./test.png");
file.open(QIODevice::WriteOnly);
m_pixmap.save(&file);
file.close();
#endif // 0
}
void TransparentRect::onMoveEvent(QMouseEvent *event,const QPoint &newPos)
{
m_scaled = QGuiApplication::screenAt(event->globalPos())->logicalDotsPerInch()/ m_startInch;
// auto newPos = event->pos();
int xOff=0,yOff=0,wOff=0,hOff=0;
int posXOff=newPos.x()-m_lastPos.x();
int posYOff = newPos.y()-m_lastPos.y();
switch (m_areaType) {
case AreaType::LeftTopArea:
xOff = posXOff;
yOff = posYOff;
break;
case AreaType::TopArea:
yOff = posYOff;
break;
case AreaType::RightTopArea:
yOff = posYOff;
wOff = posXOff;
break;
case AreaType::LeftArea:
xOff = posXOff;
break;
case AreaType::RightArea:
wOff = posXOff;
break;
case AreaType::LeftBottomArea:
xOff = posXOff;
hOff = posYOff;
break;
case AreaType::BottomArea:
hOff = posYOff;
break;
case AreaType::RightBottomArea:
wOff = posXOff;
hOff = posYOff;
break;
case AreaType::TitleArea: //move
m_rect.moveTopLeft(m_rect.topLeft()+QPoint(posXOff,posYOff));
break;
default:
break;
}
//min judget:
if(m_rect.width()-xOff<=FrameLessData::minWidth)
xOff=0;
else if(m_rect.left()<event->globalPos().x() && xOff<0)
xOff=0;
if(m_rect.width()+wOff<=FrameLessData::minWidth)
wOff=0;
else if(m_rect.right()>event->globalPos().x() && wOff>0)
wOff=0;
if(m_rect.height()-yOff<=FrameLessData::minHeight)
yOff=0;
else if(m_rect.top()<event->globalPos().y() && yOff<0)
yOff=0;
if(m_rect.height()+hOff<=FrameLessData::minHeight)
hOff=0;
else if(m_rect.bottom()>event->globalPos().y() && hOff>0)
hOff=0;
//resize:
m_rect.setTop(m_rect.top()+yOff);
m_rect.setLeft(m_rect.left()+xOff);
m_rect.setWidth(m_rect.width()+wOff);
m_rect.setHeight(m_rect.height()+hOff);
m_lastPos = newPos;
update();
}
void TransparentRect::onMouseReleaseEvent(QMouseEvent *event)
{
m_areaType=AreaType::None;
//释放pixmap,
m_pixmap = QPixmap();
//重绘,触发paintEvent,否则会有闪动,
//注意要在hide之前调用。
repaint();
}
void TransparentRect::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::HighQualityAntialiasing | QPainter::Antialiasing);
painter.setBackground(FrameLessData::transparentRectBkground);
painter.drawPixmap(0, 0, m_pixmap);
QPen pen(FrameLessData::borderStyle);
pen.setWidth(FrameLessData::borderThickness);
pen.setColor(FrameLessData::borderColor);
painter.setPen(pen);
//背景色取反,要求color必须是white
painter.setCompositionMode(QPainter::RasterOp_SourceAndNotDestination);
//rect位置相对与this的geometry
auto rect=m_rect;
rect.moveTopLeft(rect.topLeft()-geometry().topLeft());
rect.setWidth(rect.width()*m_scaled);
rect.setHeight(rect.height()*m_scaled);
painter.drawRect(rect);
painter.end();
}
#ifndef TRANSPARENTRECT_H
#define TRANSPARENTRECT_H
#include <QWidget>
#include "framelessData.h"
class TransparentRect : public QWidget
{
Q_OBJECT
public:
TransparentRect(QWidget *parent=nullptr);
void setRect(const QRect &rect);
QRect getRect() const;
void onMousePressEvent(QMouseEvent *event,AreaType type);
void onMoveEvent(QMouseEvent *event,const QPoint &pos);
void onMouseReleaseEvent(QMouseEvent *event);
protected:
virtual void paintEvent(QPaintEvent *event) override;
private:
QRect m_rect;
QPoint m_lastPos;
AreaType m_areaType;
QPixmap m_pixmap;
double m_scaled=1.0;
double m_startInch;
};
#endif // TRANSPARENTRECT_H
cmake_minimum_required(VERSION 3.5)
project(frameLessHelper VERSION 0.1 LANGUAGES CXX)
add_definitions(-DTITLEBAR)
#add_definitions(-DRUBBER)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets WebEngineWidgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets WebEngineWidgets)
file(GLOB PROJECT_SOURCES "*.cpp" "*.h" "*.ui" "framelessHelper/*.*")
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(frameLessHelper
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET frameLessHelper APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else()
if(ANDROID)
add_library(frameLessHelper SHARED
${PROJECT_SOURCES}
)
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else()
add_executable(frameLessHelper
${PROJECT_SOURCES}
framelessHelper/titleBarImg.qrc
webview.h webview.cpp
)
endif()
endif()
target_link_libraries(frameLessHelper PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::WebEngineWidgets)
set_target_properties(frameLessHelper PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
install(TARGETS frameLessHelper
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(frameLessHelper)
endif()
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QGridLayout>
#include <QWebEngineView>
#include <QFrame>
#include "webview.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_frameHelper(new FrameLessHelper(this))
{
ui->setupUi(this);
setWindowFlags(Qt::FramelessWindowHint);
installEventFilter(this);
#ifdef TITLEBAR
//titleBar
auto titleBar = m_frameHelper->getTitleBar();
if(titleBar){
QGridLayout *layout = new QGridLayout(centralWidget());
layout->setSpacing(0);
layout->setContentsMargins(0,0,0,0);
layout->addWidget(titleBar,0,0,0,0,Qt::AlignTop);
titleBar->setFixedHeight(32);
#if 0
WebView *view = new WebView();
view->load(QUrl("https:www.baidu.com"));
auto childs = view->children();
for(auto child:childs){
// if(child->metaObject()->className()==QString("QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget"))
child->installEventFilter(this);
}
view->installEventFilter(this);
#else
QFrame *view = new QFrame();
#endif
layout->addWidget(view,1,0);
}
setMouseTracking(true);
#endif
}
MainWindow::~MainWindow()
{
delete ui;
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
return m_frameHelper->eventFilter(watched,event);
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "framelessHelper/framelessHelper.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
bool eventFilter(QObject *watched, QEvent *event) override;
private:
Ui::MainWindow *ui;
FrameLessHelper *m_frameHelper;
};
#endif // MAINWINDOW_H
源码: