目的
Qt webview在下层展示url,上层覆盖最小化等按钮,支持大小拖拽,窗口移动。
如下图,除红框部分,其余异形部分作为url展示区
构想
想要实现该效果,需要在webview窗口上方动态创建两个窗口,手动实现拖动、拖拽、以及拖拽带来的位置问题。
1、拖动问题:在上方红框窗口实现拖动效果,接收到鼠标拖动时,动态设置主窗口位置。
2、拖拽问题:由于标题栏需要自己实现,所以需要设置setWindowFlags(Qt::FramelessWindowHint);以去除边框,那么需要自己实现大小拖拽。在下方红框窗口最右边添加图标,鼠标进入时变化为拖拽样式,接收到鼠标拖拽时,动态设置主窗口大小。类似Qt designer的这个角。
3、位置问题:代码创建上下红框的两个窗口,在resizeEvent事件中,动态调整位置,使其始终靠右,且宽度随主窗口的放大缩小同比例变换。
问题
实践后发现,在拖拽过程中,上方的红框窗口,可能会挡住webview的鼠标事件,因此就引出该文的鼠标透传事件。
实现
既然都需要透传事件了,那动态创建窗口就没必要了,之间选择窗口覆盖。
窗口框架
利用QStackedLayout的setStackingMode(QStackedLayout::StackAll);实现窗口覆盖,实现参考。 WidgetB即红框窗口,WidgetA表示webview,使用pushbutton判断 鼠标事件是否透传成功。
鼠标事件透传
第一反应是,由于QMouseEvent类只有位置和鼠标类型信息,与父窗口无关。 在WidgetB的mouseEvent事件中,将该event传递给WidgetA。
void WidgetB::mousePressEvent(QMouseEvent* event) {
if (Qt::LeftButton == event->button()) {
if (event->pos().y() < 40) {
move_ = true;
}
else {
move_ = false;
}
point_start_ = event->globalPos();
}
widget_a_->MouseEventHandle(event, WidgetA::MouseEventType::MousePress);
}
void WidgetB::mouseMoveEvent(QMouseEvent* event) {
if (event->buttons() & Qt::LeftButton && !point_start_.isNull() & move_) {
QPoint move_point = event->globalPos() - point_start_;
point_start_ = event->globalPos();
auto widget = parentWidget();
widget->move(widget->pos().x() + move_point.x(), widget->pos().y() + move_point.y());
}
widget_a_->MouseEventHandle(event, WidgetA::MouseEventType::MouseMove);
}
void WidgetB::mouseReleaseEvent(QMouseEvent* event) {
move_ = false;
widget_a_->MouseEventHandle(event, WidgetA::MouseEventType::MouseRelease);
}
该方法确实可以将鼠标事件传给WigetA,但由于事件先到控件再到父窗口, 因此事件无法传递给WidgetA上的各个控件。
参考鼠标事件透传
每一次鼠标事件触发后,根据位置计算出对应WidgetA上的控件。直接利用QApplication::postEvent(w, event);,将事件传递给该控件,后续是否传递给父窗口(WidgetA由控件判定)。
void WidgetB::mousePressEvent(QMouseEvent* event) {
if (Qt::LeftButton == event->button()) {
if (event->pos().y() < 40) {
move_ = true;
}
else {
move_ = false;
}
point_start_ = event->globalPos();
}
PostMouseEventToSiblings(event);
}
void WidgetB::mouseMoveEvent(QMouseEvent* event) {
if (event->buttons() & Qt::LeftButton && !point_start_.isNull() & move_) {
QPoint move_point = event->globalPos() - point_start_;
point_start_ = event->globalPos();
auto widget = parentWidget();
widget->move(widget->pos().x() + move_point.x(), widget->pos().y() + move_point.y());
}
PostMouseEventToSiblings(event);
}
void WidgetB::mouseReleaseEvent(QMouseEvent* event) {
move_ = false;
PostMouseEventToSiblings(event);
}
void WidgetB::PostMouseEventToSiblings(QMouseEvent* e) {
if (this->parentWidget()) {
this->setAttribute(Qt::WA_TransparentForMouseEvents, true);
QPoint pt = this->mapTo(this->parentWidget(), e->pos());
QWidget* w = this->parentWidget()->childAt(pt);
if (w) {
pt = w->mapFrom(this->parentWidget(), pt);
QMouseEvent* event = new QMouseEvent(e->type(), pt, e->button(),
e->buttons(), e->modifiers());
QApplication::postEvent(w, event);
}
this->setAttribute(Qt::WA_TransparentForMouseEvents, false);
}
}
效果
代码链接