Skip to content

Commit

Permalink
sync: from linuxdeepin/qt5integration
Browse files Browse the repository at this point in the history
Synchronize source files from linuxdeepin/qt5integration.

Source-pull-request: linuxdeepin/qt5integration#231
  • Loading branch information
deepin-ci-robot committed Aug 9, 2024
1 parent 0256e91 commit 8ac5f00
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 85 deletions.
2 changes: 0 additions & 2 deletions styleplugins/chameleon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ dtk_add_plugin(
chameleonstyle.h
common.h
dstyleanimation.h
RESOURCES
resources.qrc
DEPENDENCIES
Dtk${VERSION_SUFFIX}::Widget
Qt${QT_VERSION_MAJOR}::CorePrivate
Expand Down
170 changes: 102 additions & 68 deletions styleplugins/chameleon/chameleonstyle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <QBitmap>
#include <QTableView>
#include <QStyledItemDelegate>
#include <QVariantAnimation>
#include <DSpinBox>
#include <DTreeView>
#include <DIconButton>
Expand Down Expand Up @@ -122,6 +123,57 @@ inline static bool isTheClassObject(QObject *object)
#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;
}
}

ChameleonStyle::ChameleonStyle()
: DStyle()
{
Expand Down Expand Up @@ -2757,7 +2809,7 @@ bool ChameleonStyle::drawMenuBarItem(const QStyleOptionMenuItem *option, QRect &
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,50 +2818,11 @@ void ChameleonStyle::drawMenuItemBackground(const QStyleOption *option, QPainter
painter->setPen(Qt::NoPen);
painter->setBrush(getColor(option, QPalette::Highlight));
painter->drawRect(option->rect);
return;
return nullptr;
}

// 清理旧的阴影
if (option->styleObject) {
const QRect shadow = option->styleObject->property("_d_menu_shadow_rect").toRect();
const QRect shadow_base = option->styleObject->property("_d_menu_shadow_base_rect").toRect();

// 如果当前菜单项时已选中的,并且shadow_base不等于当前区域,此时应当清理阴影区域
// 如果当前要绘制的item是触发阴影绘制的那一项,那么,此时应当清空阴影区域
if ((selected && shadow_base != option->rect)
|| (!selected && shadow_base == option->rect)
|| (!selected && shadow_base.width() != option->rect.width())) {
// 清空阴影区域
option->styleObject->setProperty("_d_menu_shadow_rect", QVariant());
option->styleObject->setProperty("_d_menu_shadow_base_rect", QVariant());

// 确保阴影区域能重绘
if (QWidget *w = qobject_cast<QWidget*>(option->styleObject)) {
w->update(shadow);
}
}
}

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

// draw shadow
if (type == QStyleOptionMenuItem::Normal) {
if (option->styleObject) {
QRect shadow(0, 0, option->rect.width(), 7);
shadow.moveTop(option->rect.bottom() + 1);
option->styleObject->setProperty("_d_menu_shadow_rect", shadow);
option->styleObject->setProperty("_d_menu_shadow_base_rect", option->rect);

// 确保阴影区域能重绘
if (QWidget *w = qobject_cast<QWidget*>(option->styleObject)) {
w->update(shadow);
}
}
}

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

if (color.color().isValid() && color.color().alpha() != 0) {
Expand Down Expand Up @@ -2846,35 +2859,49 @@ void ChameleonStyle::drawMenuItemBackground(const QStyleOption *option, QPainter
}

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

// 为上一个item绘制阴影
const QRect shadow = option->styleObject->property("_d_menu_shadow_rect").toRect();
} while (false);
}
{ // 无论如何都尝试绘制,因为可能有动画存在
color = option->palette.highlight();

// 判断阴影rect是否在自己的区域
if (!option->rect.contains(shadow.center()))
return;
QWidget *animationTargetWidget = qobject_cast<QWidget*>(option->styleObject);
if (!option->styleObject)
animationTargetWidget = dynamic_cast<QWidget*>(painter->device());

ChameleonMovementAnimation *animation = nullptr;

static QColor shadow_color;
static QPixmap shadow_pixmap;
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 (shadow_color != option->palette.color(QPalette::Active, QPalette::Highlight)) {
shadow_color = option->palette.color(QPalette::Active, QPalette::Highlight);
QImage image(":/chameleonstyle/menu_shadow.svg");
QPainter pa(&image);
pa.setCompositionMode(QPainter::CompositionMode_SourceIn);
pa.fillRect(image.rect(), shadow_color);
shadow_pixmap = QPixmap::fromImage(image);
if (selected)
animation->setTargetRect(option->rect);
}
const int round = 6;
if (animation && animation->isRuning()) {
auto opacity = painter->opacity();
// 一些状态为 disable 的 menu item 在绘制时会修改不透明度,这里暂时改回1.0。
painter->setOpacity(1.0);
painter->setBrush(color);
painter->setPen(Qt::NoPen);
painter->drawRoundedRect(animation->currentRect(), round, round);
painter->setOpacity(opacity);

if (!shadow_pixmap.isNull()) {
if (QMenu *menu = qobject_cast<QMenu *>(option->styleObject)) {
if (!menu->geometry().contains(QCursor::pos()))
return;
}
painter->drawPixmap(shadow, shadow_pixmap);
return animation;
} else if (selected) {
painter->setBrush(color);
painter->setPen(Qt::NoPen);
painter->drawRoundedRect(option->rect, round, round);
}
}

return nullptr;
}

void ChameleonStyle::drawMenuItemRedPoint(const QStyleOptionMenuItem *option, QPainter *painter, const QWidget *widget) const
Expand Down Expand Up @@ -2955,7 +2982,7 @@ bool ChameleonStyle::drawMenuItem(const QStyleOptionMenuItem *option, QPainter *
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,6 +2999,13 @@ bool ChameleonStyle::drawMenuItem(const QStyleOptionMenuItem *option, QPainter *
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 3010 in styleplugins/chameleon/chameleonstyle.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Condition '!ignoreCheckMark' is always true

Expand All @@ -2990,7 +3024,7 @@ bool ChameleonStyle::drawMenuItem(const QStyleOptionMenuItem *option, QPainter *
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 +3044,7 @@ bool ChameleonStyle::drawMenuItem(const QStyleOptionMenuItem *option, QPainter *

}

if (selected) {
if (useHighlightedText) {
painter->setPen(getColor(option, QPalette::HighlightedText));
} else {
if ((option->state & QStyle::State_Enabled)) {
Expand Down Expand Up @@ -4196,7 +4230,7 @@ int ChameleonStyle::pixelMetric(QStyle::PixelMetric m, const QStyleOption *opt,
case PM_MenuVMargin:
return 8;
case PM_MenuHMargin:
return 0;
return 6;
default:
break;
}
Expand Down
30 changes: 29 additions & 1 deletion styleplugins/chameleon/chameleonstyle.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define CHAMELEONSTYLE_H

#include <DStyle>
#include <QVariantAnimation>

DWIDGET_USE_NAMESPACE

Expand All @@ -18,6 +19,33 @@ class DStyleOptionButtonBoxButton;
DWIDGET_END_NAMESPACE

namespace chameleon {
class ChameleonMovementAnimation : public QVariantAnimation
{
Q_OBJECT

public:
explicit ChameleonMovementAnimation(QWidget *targetWidget);

inline QRect currentRect() const {
return m_currentRect;
}

inline bool isRuning() const {
return state() == QVariantAnimation::Running;
}

inline float progress() const {
return float(currentLoopTime()) / duration();
}

QWidget *targetWidget() const;
void setTargetRect(const QRect &rect);

private:
QRect m_currentRect;
QRect m_targetRect;
QRect m_lastTargetRect;
};

class ChameleonStyle : public DStyle
{
Expand Down Expand Up @@ -67,7 +95,7 @@ class ChameleonStyle : public DStyle
bool drawSpinBox(const QStyleOptionSpinBox *opt, QPainter *p, const QWidget *w) const;
void updateSpinBoxButtonState(const QStyleOptionSpinBox *opt, QStyleOptionButton& button, bool isActive, bool isEnabled) const;
bool drawMenuBarItem(const QStyleOptionMenuItem *option, QRect &rect, QPainter *painter, const QWidget *widget) const;
void drawMenuItemBackground(const QStyleOption *option, QPainter *painter, QStyleOptionMenuItem::MenuItemType type) const;
ChameleonMovementAnimation *drawMenuItemBackground(const QStyleOption *option, QPainter *painter, QStyleOptionMenuItem::MenuItemType type) const;
bool drawMenuItem(const QStyleOptionMenuItem *option, QPainter *painter, const QWidget *widget) const;
bool drawTabBar(QPainter *painter ,const QStyleOptionTab *tab, const QWidget *widget) const;
bool drawTabBarLabel(QPainter *painter ,const QStyleOptionTab *tab, const QWidget *widget) const;
Expand Down
9 changes: 0 additions & 9 deletions styleplugins/chameleon/menu_shadow.svg

This file was deleted.

5 changes: 0 additions & 5 deletions styleplugins/chameleon/resources.qrc

This file was deleted.

0 comments on commit 8ac5f00

Please sign in to comment.