dde-dock/frame/item/components/floatingpreview.cpp
yanghongwei 3960e7b359 fix: 解决任务栏在预览关闭时崩溃问题
1.通过略缩图关闭驻留应用,任务栏崩溃.由于使用了野指针,导致概率性崩溃。
2.非驻留时,关闭最后一个预览时AppItem对象被析构,但是之前connect没有指定receiver。导致信号还是被响应,使用了野指针。

Log: 修复任务栏在缩略图状态下关闭应用崩溃问题
Bug: https://pms.uniontech.com/zentao/bug-view-89275.html
Change-Id: Ib652beb4698193c33df9ed465cf843ceefeaa6ec
2021-07-29 17:45:18 +08:00

203 lines
6.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
*
* Author: sbw <sbw@sbw.so>
*
* Maintainer: sbw <sbw@sbw.so>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "floatingpreview.h"
#include "appsnapshot.h"
#include "previewcontainer.h"
#include <DStyle>
#include <QGraphicsEffect>
#include <QPainter>
#include <QVBoxLayout>
#define BORDER_MARGIN 8
#define TITLE_MARGIN 20
#define BTN_TITLE_MARGIN 6
FloatingPreview::FloatingPreview(QWidget *parent)
: QWidget(parent)
, m_closeBtn3D(new DIconButton(this))
, m_titleBtn(new DPushButton(this))
{
m_closeBtn3D->setObjectName("closebutton-3d");
m_closeBtn3D->setFixedSize(24, 24);
m_closeBtn3D->setIconSize(QSize(24, 24));
m_closeBtn3D->setIcon(QIcon(":/icons/resources/close_round_normal.svg"));
m_closeBtn3D->setFlat(true);
m_closeBtn3D->installEventFilter(this);
m_titleBtn->setBackgroundRole(QPalette::Base);
m_titleBtn->setForegroundRole(QPalette::Text);
m_titleBtn->setFocusPolicy(Qt::NoFocus);
m_titleBtn->setAttribute(Qt::WA_TransparentForMouseEvents);
QVBoxLayout *centralLayout = new QVBoxLayout;
centralLayout->addWidget(m_closeBtn3D);
centralLayout->setAlignment(m_closeBtn3D, Qt::AlignRight | Qt::AlignTop);
centralLayout->addWidget(m_titleBtn);
centralLayout->setAlignment(m_titleBtn, Qt::AlignCenter | Qt::AlignBottom);
centralLayout->addSpacing(TITLE_MARGIN);
centralLayout->setMargin(0);
centralLayout->setSpacing(0);
setLayout(centralLayout);
setFixedSize(SNAP_WIDTH, SNAP_HEIGHT);
connect(m_closeBtn3D, &DIconButton::clicked, this, &FloatingPreview::onCloseBtnClicked);
}
WId FloatingPreview::trackedWid() const
{
Q_ASSERT(!m_tracked.isNull());
return m_tracked->wid();
}
AppSnapshot *FloatingPreview::trackedWindow()
{
return m_tracked;
}
void FloatingPreview::trackWindow(AppSnapshot *const snap)
{
if (!snap)
return;
if (!m_tracked.isNull())
m_tracked->removeEventFilter(this);
snap->installEventFilter(this);
m_tracked = snap;
m_closeBtn3D->setVisible(m_tracked->closeAble());
QFontMetrics fm(m_titleBtn->font());
int textWidth = fm.width(m_tracked->title()) + 10 + BTN_TITLE_MARGIN;
int titleWidth = width() - (TITLE_MARGIN * 2 + BORDER_MARGIN);
if (textWidth < titleWidth) {
m_titleBtn->setFixedWidth(textWidth);
m_titleBtn->setText(m_tracked->title());
} else {
QString str = m_tracked->title();
/*某些特殊字符只显示一半 如"Q"," W",所以加一个空格保证字符显示完整,*/
str.insert(0, " ");
QString strTtile = m_titleBtn->fontMetrics().elidedText(str, Qt::ElideRight, titleWidth - BTN_TITLE_MARGIN);
m_titleBtn->setText(strTtile);
m_titleBtn->setFixedWidth(titleWidth + BTN_TITLE_MARGIN);
}
QTimer::singleShot(0, this, [ = ] {
// 此处获取的snap->geometry()有可能是错误的所以做个判断并且在resizeEvent中也做处理
if(snap->width() == SNAP_WIDTH)
setGeometry(snap->geometry());
});
}
void FloatingPreview::paintEvent(QPaintEvent *e)
{
QWidget::paintEvent(e);
if (m_tracked.isNull())
return;
const QImage &snapshot = m_tracked->snapshot();
const QRectF &snapshot_geometry = m_tracked->snapshotGeometry();
if (snapshot.isNull())
return;
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
const QRectF r = rect().marginsRemoved(QMargins(BORDER_MARGIN, BORDER_MARGIN, BORDER_MARGIN, BORDER_MARGIN));
const auto ratio = devicePixelRatioF();
const qreal offset_x = width() / 2.0 - snapshot_geometry.width() / ratio / 2 - snapshot_geometry.left() / ratio;
const qreal offset_y = height() / 2.0 - snapshot_geometry.height() / ratio / 2 - snapshot_geometry.top() / ratio;
DStyleHelper dstyle(style());
const int radius = dstyle.pixelMetric(DStyle::PM_FrameRadius);
// 选中外框
QPen pen;
pen.setColor(palette().highlight().color());
pen.setWidth(dstyle.pixelMetric(DStyle::PM_FocusBorderWidth));
painter.setPen(pen);
painter.setBrush(Qt::NoBrush);
painter.drawRoundedRect(r, radius, radius);
}
void FloatingPreview::mouseReleaseEvent(QMouseEvent *e)
{
QWidget::mouseReleaseEvent(e);
if (m_tracked) {
emit m_tracked->clicked(m_tracked->wid());
}
}
bool FloatingPreview::eventFilter(QObject *watched, QEvent *event)
{
if(watched == m_closeBtn3D) {
if(watched == m_closeBtn3D && (event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverMove)) {
m_closeBtn3D->setIcon(QIcon(":/icons/resources/close_round_hover.svg"));
}
else if (watched == m_closeBtn3D && event->type() == QEvent::HoverLeave) {
m_closeBtn3D->setIcon(QIcon(":/icons/resources/close_round_normal.svg"));
}
else if (watched == m_closeBtn3D && event->type() == QEvent::MouseButtonPress) {
m_closeBtn3D->setIcon(QIcon(":/icons/resources/close_round_press.svg"));
}
}
if (watched == m_tracked) {
if (event->type() == QEvent::Destroy) {
// 此处需要置空否则当Destroy事件响应结束后会在FloatingPreview::hideEvent使用m_tracked野指针
m_tracked = nullptr;
hide();
}
if (event->type() == QEvent::Resize && m_tracked->width() == SNAP_WIDTH)
setGeometry(m_tracked->geometry());
}
return QWidget::eventFilter(watched, event);
}
void FloatingPreview::hideEvent(QHideEvent *event)
{
if (m_tracked) {
m_tracked->setContentsMargins(0, 0, 0, 0);
m_tracked->setWindowState();
}
QWidget::hideEvent(event);
}
void FloatingPreview::onCloseBtnClicked()
{
Q_ASSERT(!m_tracked.isNull());
m_tracked->closeWindow();
}