From b16b768b782aa4f6c01ce17cb4605d7c98a1463c Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Tue, 9 Jul 2024 07:59:49 +0000 Subject: [PATCH] sync: from linuxdeepin/qt5integration Synchronize source files from linuxdeepin/qt5integration. Source-pull-request: https://github.com/linuxdeepin/qt5integration/pull/231 --- CMakeLists.txt | 21 ++- platformthemeplugin/CMakeLists.txt | 4 +- styleplugins/chameleon/chameleonstyle.cpp | 168 +++++++++++++--------- styleplugins/chameleon/chameleonstyle.h | 30 +++- 4 files changed, 150 insertions(+), 73 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e62a97..3a7cb0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,12 +103,23 @@ function(dtk_add_plugin) endfunction() find_package(Dtk${VERSION_SUFFIX} REQUIRED COMPONENTS Widget) -cmake_dependent_option(ENABLE_QT_XDG_ICON_LOADER "Enable QtXdgIconLoader" ON "QT_VERSION_MAJOR EQUAL 5" OFF) + +option(ENABLE_QT_XDG_ICON_LOADER "Enable QtXdgIconLoader" ON) +find_package(Qt${QT_VERSION_MAJOR}XdgIconLoader) +find_package(Qt${QT_VERSION_MAJOR}Xdg REQUIRED) +if (NOT Qt${QT_VERSION_MAJOR}XdgIconLoader_FOUND) + message(WARNING " Qt${QT_VERSION_MAJOR}XdgIconLoader Not Found, DISABLE QtXdgIconLoader !") + set (ENABLE_QT_XDG_ICON_LOADER OFF) +endif() + if(ENABLE_QT_XDG_ICON_LOADER) - find_package(Qt5XdgIconLoader REQUIRED) - find_package(Qt5Xdg REQUIRED) - set(QT_XDG_ICON_DEPS Qt5XdgIconLoader Qt5Xdg) - set(XDG_ICON_VERSION_MAJOR ${Qt5XdgIconLoader_VERSION_MAJOR}) + set(QT_XDG_ICON_DEPS Qt${QT_VERSION_MAJOR}XdgIconLoader Qt${QT_VERSION_MAJOR}Xdg) + + if("${QT_VERSION_MAJOR}" STREQUAL "5") + set(XDG_ICON_VERSION_MAJOR ${Qt5XdgIconLoader_VERSION_MAJOR}) + elseif("${QT_VERSION_MAJOR}" STREQUAL "6") + set(XDG_ICON_VERSION_MAJOR ${Qt6XdgIconLoader_VERSION_MAJOR}) + endif() endif() set(PLUGIN_OUTPUT_BASE_DIR ${CMAKE_BINARY_DIR}/plugins CACHE STRING "Plugin output base path") diff --git a/platformthemeplugin/CMakeLists.txt b/platformthemeplugin/CMakeLists.txt index 4ff3203..054739b 100644 --- a/platformthemeplugin/CMakeLists.txt +++ b/platformthemeplugin/CMakeLists.txt @@ -6,7 +6,7 @@ find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS DBus Widgets) set(QT_LIBS Qt${QT_VERSION_MAJOR}::DBus Qt${QT_VERSION_MAJOR}::Widgets) if(QT_VERSION_MAJOR EQUAL 5) find_package(Qt5X11Extras REQUIRED) - list(APPEND QT_LIBS Qt5::X11Extras ${QT_XDG_ICON_DEPS}) + list(APPEND QT_LIBS Qt5::X11Extras) if(QT_VERSION_MINOR GREATER_EQUAL 8) # Qt5.8 find_package(Qt5ThemeSupport REQUIRED) list(APPEND QT_LIBS Qt5::ThemeSupportPrivate) @@ -26,10 +26,12 @@ if(QT_VERSION_MAJOR EQUAL 5) else() list(APPEND QT_LIBS Qt6::GuiPrivate) endif() + find_package(PkgConfig REQUIRED) pkg_check_modules(Deps REQUIRED IMPORTED_TARGET mtdev x11) if(ENABLE_QT_XDG_ICON_LOADER) set(DEFS -DXDG_ICON_VERSION_MAR=${XDG_ICON_VERSION_MAJOR}) + list(APPEND QT_LIBS ${QT_XDG_ICON_DEPS}) endif() if(QT_VERSION_MAJOR EQUAL 5) qt5_add_dbus_interface(DBUS_INTERFACES ${CMAKE_CURRENT_LIST_DIR}/xmls/com.deepin.filemanager.filedialog.xml filedialog_interface) diff --git a/styleplugins/chameleon/chameleonstyle.cpp b/styleplugins/chameleon/chameleonstyle.cpp index 7c72abb..2602b69 100644 --- a/styleplugins/chameleon/chameleonstyle.cpp +++ b/styleplugins/chameleon/chameleonstyle.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -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(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() { @@ -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; @@ -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(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(option->styleObject)) { - w->update(shadow); - } - } - } - - painter->fillRect(option->rect, color); - } else { + } else do { color = option->palette.window().color(); if (color.color().isValid() && color.color().alpha() != 0) { @@ -2846,35 +2859,51 @@ 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); - // 判断阴影rect是否在自己的区域 - if (!option->rect.contains(shadow.center())) - return; + { // 无论如何都尝试绘制,因为可能有动画存在 + color = option->palette.highlight(); - static QColor shadow_color; - static QPixmap shadow_pixmap; + QWidget *animationTargetWidget = qobject_cast(option->styleObject); + if (!option->styleObject) + animationTargetWidget = dynamic_cast(painter->device()); - 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); - } + ChameleonMovementAnimation *animation = nullptr; - if (!shadow_pixmap.isNull()) { - if (QMenu *menu = qobject_cast(option->styleObject)) { - if (!menu->geometry().contains(QCursor::pos())) - return; + if (animationTargetWidget) { + animation = animationTargetWidget->findChild("_d_menu_select_animation", + Qt::FindDirectChildrenOnly); + if (!animation) { + animation = new ChameleonMovementAnimation(animationTargetWidget); + animation->setObjectName("_d_menu_select_animation"); } - painter->drawPixmap(shadow, shadow_pixmap); + + if (selected) + animation->setTargetRect(option->rect); + } + + if (animation && animation->isRuning()) { + auto opacity = painter->opacity(); + // 一些状态为 disable 的 menu item 在绘制时会修改不透明度,这里暂时改回1.0。 + painter->setOpacity(1.0); + painter->setBrush(color); + painter->setPen(Qt::NoPen); + QRect rect = animation->currentRect().adjusted(6, 0, -6, 0); + painter->drawRoundedRect(rect, 6, 6); + painter->setOpacity(opacity); + + return animation; + } else if (selected) { + painter->setBrush(color); + painter->setPen(Qt::NoPen); + QRect rect = option->rect.adjusted(6, 0, -6, 0); + painter->drawRoundedRect(rect, 6, 6); } } + + return nullptr; } void ChameleonStyle::drawMenuItemRedPoint(const QStyleOptionMenuItem *option, QPainter *painter, const QWidget *widget) const @@ -2955,7 +2984,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) { @@ -2972,6 +3001,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; @@ -2990,7 +3026,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)); @@ -3010,7 +3046,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)) { diff --git a/styleplugins/chameleon/chameleonstyle.h b/styleplugins/chameleon/chameleonstyle.h index bb7276d..67d576f 100644 --- a/styleplugins/chameleon/chameleonstyle.h +++ b/styleplugins/chameleon/chameleonstyle.h @@ -6,6 +6,7 @@ #define CHAMELEONSTYLE_H #include +#include DWIDGET_USE_NAMESPACE @@ -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 { @@ -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;