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 #39

Merged
merged 1 commit into from
Sep 25, 2024
Merged
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
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
174 changes: 106 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 @@
#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 @@
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 @@
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,53 @@
}

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()) {
painter->save();
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);
painter->restore();

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->save();
painter->setBrush(color);
painter->setPen(Qt::NoPen);
painter->drawRoundedRect(option->rect, round, round);
painter->restore();
}
}

return nullptr;
}

void ChameleonStyle::drawMenuItemRedPoint(const QStyleOptionMenuItem *option, QPainter *painter, const QWidget *widget) const
Expand Down Expand Up @@ -2955,7 +2986,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 +3003,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 3014 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 +3028,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 +3048,7 @@

}

if (selected) {
if (useHighlightedText) {
painter->setPen(getColor(option, QPalette::HighlightedText));
} else {
if ((option->state & QStyle::State_Enabled)) {
Expand Down Expand Up @@ -4196,7 +4234,7 @@
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.

Loading