Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sync: from linuxdeepin/qt5integration #41

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 244 additions & 15 deletions styleplugins/chameleon/chameleonstyle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
#include <QBitmap>
#include <QTableView>
#include <QStyledItemDelegate>
#include <QVariantAnimation>
#include <QProgressBar>
#include <QTimer>
#include <DSpinBox>
#include <DTreeView>
#include <DIconButton>
Expand Down Expand Up @@ -122,6 +125,66 @@
#endif
}

ChameleonMovementAnimation::ChameleonMovementAnimation(QWidget *targetWidget)
: QVariantAnimation(targetWidget)
{
setDuration(150);

connect(this, &QVariantAnimation::valueChanged, targetWidget, [this] (const QVariant &value) {
if (!isRuning())
return;

const auto rect = value.toRect();
Q_ASSERT(!m_currentRect.isEmpty());
this->targetWidget()->update(m_currentRect.united(rect));
m_currentRect = rect;
});
connect(this, &QVariantAnimation::finished, targetWidget, [this] {
Q_ASSERT(m_currentRect == m_targetRect);
// 确保动画结束后有一帧的刷新,因为在菜单的动画过程中会修改菜单文字的 opacity
// 对opacity的修改会根据是否处于动画状态进行判断,因此要确保动画结束后刷新它
this->targetWidget()->update(m_currentRect);
});
}

QWidget *ChameleonMovementAnimation::targetWidget() const
{
return qobject_cast<QWidget*>(parent());
}

void ChameleonMovementAnimation::setTargetRect(const QRect &rect)
{
if (m_targetRect == rect)
return;

m_lastTargetRect = m_targetRect;
m_targetRect = rect;

if (m_currentRect.isEmpty())
m_currentRect = m_lastTargetRect;

// 当目标绘制区域改变时,说明当前正在进行的动画过期了,应该重新开始动画
stop();
setStartValue(m_currentRect);
setEndValue(rect);

if (!m_currentRect.isEmpty()) {
start();
} else {
// 这种情况说明不需要进行动画,往往发生在首次显示,这时候应该直接绘制到目标区域
m_currentRect = rect;
}
}

void ChameleonMovementAnimation::setCurrentRect(const QRect &rect)
{
if (m_currentRect == rect)
return;

m_currentRect = rect;
m_targetRect = QRect();
}

ChameleonStyle::ChameleonStyle()
: DStyle()
{
Expand Down Expand Up @@ -1370,7 +1433,17 @@
frameRadius = qMin(height / 2, 4);
}
p->setBrush(getColor(opt, DPalette::ObviousBackground, w));
p->setPen(Qt::NoPen);
p->drawRoundedRect(opt->rect, frameRadius, frameRadius);

QPen pen;
pen.setColor(DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::DarkType
? QColor(255, 255, 255, 0.15 * 255)
: QColor(0, 0, 0, 0.15 * 255));
pen.setWidth(1);
p->setPen(pen);
p->setBrush(Qt::NoBrush);
p->drawRoundedRect(opt->rect.marginsRemoved(QMargins(1, 1, 1, 1)), frameRadius, frameRadius);
}
return;
}
Expand Down Expand Up @@ -1410,15 +1483,31 @@
linear.setColorAt(0, startColor);
linear.setColorAt(1, endColor);
linear.setSpread(QGradient::PadSpread);
p->setBrush(QBrush(linear));
p->setBrush(startColor);

if (progBar->textVisible) {
QPainterPath pathRect;
pathRect.addRect(rect);
QPainterPath pathRoundRect;
pathRoundRect.addRoundedRect(opt->rect, frameRadius, frameRadius);
QPainterPath inter = pathRoundRect.intersected(pathRect);

QPainterPath clipPath;
clipPath.addRoundedRect(rect, frameRadius, frameRadius);
p->setClipPath(clipPath);
p->setClipping(true);
p->drawPath(inter);
p->setClipping(false);

QPen pen;
pen.setColor(DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::DarkType
? QColor(255, 255, 255, 0.3 * 255)
: QColor(0, 0, 0, 0.3 * 255));
pen.setWidth(1);
p->setPen(pen);
p->setBrush(Qt::NoBrush);
p->drawRoundedRect(rect.marginsRemoved(QMargins(1, 1, 1, 1)), frameRadius, frameRadius);

} else {
//进度条高度 <= 8px && 进度条宽度 <= 8px && value有效
if (rect.height() <= ProgressBar_MinimumStyleHeight &&
Expand All @@ -1444,8 +1533,109 @@
path2.arcTo(endRect, 90, -180);
p->drawPath(path2);
}
} else
} else {
QPainterPath clipPath;
clipPath.addRoundedRect(opt->rect, frameRadius, frameRadius);
p->setClipPath(clipPath);
p->setClipping(true);
p->drawRoundedRect(rect, frameRadius, frameRadius);
p->setClipping(false);

QPen pen;
pen.setColor(QColor(0, 0, 0, 0.3 * 255));
pen.setWidth(1);
p->setPen(pen);
p->setBrush(Qt::NoBrush);
p->setClipping(true);
p->drawRoundedRect(rect.marginsRemoved(QMargins(1, 1, 1, 1)), frameRadius, frameRadius);
p->setClipping(false);
}
}

// 进度条光斑
auto progressbar = qobject_cast<QProgressBar *>(opt->styleObject);
if (!progressbar)
progressbar = dynamic_cast<QProgressBar *>(p->device());

if (!progressbar)
return;

bool isHorizontal = (progressbar->orientation() == Qt::Horizontal);

constexpr int spotWidth = 200;
if (isHorizontal ? rect.width() >= spotWidth : rect.height() >= spotWidth) {
p->setPen(Qt::NoPen);

ChameleonMovementAnimation *progressAnimation = nullptr;

progressAnimation = progressbar->findChild<ChameleonMovementAnimation *>("_d_progress_spot_animation",
Qt::FindDirectChildrenOnly);
if (!progressAnimation) {
progressAnimation = new ChameleonMovementAnimation(progressbar);
progressAnimation->setObjectName("_d_progress_spot_animation");
}

QColor shadowColor(0, 0, 0, int(0.15 * 255));
QColor spotColor(255, 255, 255, int(0.5 * 255));
QColor highLightColor(getColor(opt, DPalette::Highlight));

QPointF pointStart, pointEnd;
if (isHorizontal) {
pointStart = QPointF(progressAnimation->currentValue().toRect().left(), progressAnimation->currentValue().toRect().center().y());
pointEnd = QPointF(progressAnimation->currentValue().toRect().right(), progressAnimation->currentValue().toRect().center().y());
} else {
pointStart = QPointF(progressAnimation->currentValue().toRect().center().x(), progressAnimation->currentValue().toRect().bottom());
pointEnd = QPointF(progressAnimation->currentValue().toRect().center().x(), progressAnimation->currentValue().toRect().top());
}
QLinearGradient linear(pointStart, pointEnd);
linear.setColorAt(0, highLightColor);
linear.setColorAt(0.35, shadowColor);
linear.setColorAt(0.5, spotColor);
linear.setColorAt(0.65, shadowColor);
linear.setColorAt(1, highLightColor);
linear.setSpread(QGradient::PadSpread);
linear.setInterpolationMode(QLinearGradient::InterpolationMode::ColorInterpolation);
p->setBrush(linear);

QPainterPath clipPath;
clipPath.addRoundedRect(rect, frameRadius, frameRadius);
p->setClipping(true);
p->drawRect(progressAnimation->currentValue().toRect().marginsRemoved(QMargins(1, 1, 1, 1)));
p->setClipping(false);

if (progressAnimation->state() == QVariantAnimation::Running) {
QRect startRect, endRect;
if (isHorizontal) {
startRect = QRect(rect.x() - spotWidth, rect.y(), spotWidth, rect.height());
endRect = startRect;
endRect.moveRight(rect.width() + spotWidth);
} else {
endRect = QRect(rect.x(), rect.y() - spotWidth, rect.width(), spotWidth);
startRect = endRect;
startRect.moveTop(rect.bottom());
}
progressAnimation->setTargetRect(endRect);
return;
}

// 动画之间需要间隔1s
QTimer::singleShot(1000, progressAnimation, [progressAnimation, isHorizontal, rect]() {
QRect startRect, endRect;
if (isHorizontal) {
startRect = QRect(rect.x() - spotWidth, rect.y(), spotWidth, rect.height());
endRect = startRect;
endRect.moveRight(rect.width() + spotWidth);
} else {
endRect = QRect(rect.x(), rect.y() - spotWidth, rect.width(), spotWidth);
startRect = endRect;
startRect.moveTop(rect.bottom());
}

progressAnimation->setDuration(2500);
progressAnimation->setEasingCurve(QEasingCurve::InQuad);
progressAnimation->setCurrentRect(startRect);
progressAnimation->setTargetRect(endRect);
});
}
}
return;
Expand Down Expand Up @@ -2757,7 +2947,7 @@
return true;
}

void ChameleonStyle::drawMenuItemBackground(const QStyleOption *option, QPainter *painter, QStyleOptionMenuItem::MenuItemType type) const
ChameleonMovementAnimation *ChameleonStyle::drawMenuItemBackground(const QStyleOption *option, QPainter *painter, QStyleOptionMenuItem::MenuItemType type) const
{
QBrush color;
bool selected = (option->state & QStyle::State_Enabled) && option->state & QStyle::State_Selected;
Expand All @@ -2766,7 +2956,7 @@
painter->setPen(Qt::NoPen);
painter->setBrush(getColor(option, QPalette::Highlight));
painter->drawRect(option->rect);
return;
return nullptr;
}

// 清理旧的阴影
Expand All @@ -2791,8 +2981,6 @@
}

if (selected) {
color = option->palette.highlight();

// draw shadow
if (type == QStyleOptionMenuItem::Normal) {
if (option->styleObject) {
Expand All @@ -2807,9 +2995,7 @@
}
}
}

painter->fillRect(option->rect, color);
} else {
} else do {
color = option->palette.window().color();

if (color.color().isValid() && color.color().alpha() != 0) {
Expand Down Expand Up @@ -2846,14 +3032,14 @@
}

if (!option->styleObject)
return;
break;

// 为上一个item绘制阴影
const QRect shadow = option->styleObject->property("_d_menu_shadow_rect").toRect();

// 判断阴影rect是否在自己的区域
if (!option->rect.contains(shadow.center()))
return;
break;

static QColor shadow_color;
static QPixmap shadow_pixmap;
Expand All @@ -2870,11 +3056,47 @@
if (!shadow_pixmap.isNull()) {
if (QMenu *menu = qobject_cast<QMenu *>(option->styleObject)) {
if (!menu->geometry().contains(QCursor::pos()))
return;
break;
}
painter->drawPixmap(shadow, shadow_pixmap);
}
} while (false);

{ // 无论如何都尝试绘制,因为可能有动画存在
color = option->palette.highlight();

QWidget *animationTargetWidget = qobject_cast<QWidget*>(option->styleObject);
if (!option->styleObject)
animationTargetWidget = dynamic_cast<QWidget*>(painter->device());

ChameleonMovementAnimation *animation = nullptr;

if (animationTargetWidget) {
animation = animationTargetWidget->findChild<ChameleonMovementAnimation*>("_d_menu_select_animation",
Qt::FindDirectChildrenOnly);
if (!animation) {
animation = new ChameleonMovementAnimation(animationTargetWidget);
animation->setObjectName("_d_menu_select_animation");
}

if (selected)
animation->setTargetRect(option->rect);
}

if (animation && animation->isRuning()) {
auto opacity = painter->opacity();
// 一些状态为 disable 的 menu item 在绘制时会修改不透明度,这里暂时改回1.0。
painter->setOpacity(1.0);
painter->fillRect(animation->currentRect(), color);
painter->setOpacity(opacity);

return animation;
} else if (selected) {
painter->fillRect(option->rect, color);
}
}

return nullptr;
}

void ChameleonStyle::drawMenuItemRedPoint(const QStyleOptionMenuItem *option, QPainter *painter, const QWidget *widget) const
Expand Down Expand Up @@ -2955,7 +3177,7 @@
bool sunken = menuItem->state & State_Sunken;

//绘制背景
drawMenuItemBackground(option, painter, menuItem->menuItemType);
auto animation = drawMenuItemBackground(option, painter, menuItem->menuItemType);

//绘制分段
if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
Expand All @@ -2972,8 +3194,15 @@
return true;
}

const bool useHighlightedText = selected && !animation;
if (!useHighlightedText && selected) {
// 在动画中时,selected item 的文字颜色不会使用 HighlightedText,当动画结束后会立即
// 变为 HighlightedText,会显得比较突然,因此使用不透明度对文本等内容进行过渡
painter->setOpacity(1.0 - animation->progress());
}

//绘制选择框
bool ignoreCheckMark = false;

Check warning on line 3205 in styleplugins/chameleon/chameleonstyle.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Condition '!ignoreCheckMark' is always true

int frameRadius = DStyle::pixelMetric(PM_FrameRadius); //打钩矩形的左侧距离item的左边缘; 也是 打钩矩形的右侧距离 图文内容的左边缘
int smallIconSize = proxy()->pixelMetric(PM_ButtonIconSize, option, widget);//打钩的宽度
Expand All @@ -2990,7 +3219,7 @@
checkRect.moveCenter(QPoint(checkRect.left() + smallIconSize / 2, menuItem->rect.center().y()));
painter->setRenderHint(QPainter::Antialiasing);

if (selected)
if (useHighlightedText)
painter->setPen(getColor(option, QPalette::HighlightedText));
else
painter->setPen(getColor(option, QPalette::BrightText));
Expand All @@ -3010,7 +3239,7 @@

}

if (selected) {
if (useHighlightedText) {
painter->setPen(getColor(option, QPalette::HighlightedText));
} else {
if ((option->state & QStyle::State_Enabled)) {
Expand Down
Loading
Loading