diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b46e7a..443d28b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,6 +107,7 @@ set(SOURCE_FILES src/utils/power-button.cpp src/utils/power-button.h src/utils/app-manager.cpp src/utils/app-manager.h src/menu/menu-manager.cpp src/menu/menu-manager.h + src/items/theme-icon.h src/items/theme-icon.cpp ) # qrc文件 diff --git a/qml/AppControls2/RoundButton.qml b/qml/AppControls2/RoundButton.qml index 2815a4c..ddae06b 100644 --- a/qml/AppControls2/RoundButton.qml +++ b/qml/AppControls2/RoundButton.qml @@ -14,7 +14,7 @@ MouseArea { alpha: buttonMouseArea.containsPress ? 0.17 : buttonMouseArea.containsMouse ? 0.12 : 0.06 } - Image { + ThemeIcon { anchors.centerIn: parent width: 16; height: width source: buttonIcon diff --git a/src/appdata/app-icon-provider.cpp b/src/appdata/app-icon-provider.cpp index aedb1fb..12c1f69 100644 --- a/src/appdata/app-icon-provider.cpp +++ b/src/appdata/app-icon-provider.cpp @@ -33,6 +33,11 @@ AppIconProvider::AppIconProvider() : QQuickImageProvider(QQmlImageProviderBase:: } QPixmap AppIconProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) +{ + return AppIconProvider::getPixmap(id, size, requestedSize); +} + +QPixmap AppIconProvider::getPixmap(const QString &id, QSize *size, const QSize &requestedSize) { // 不处理sourceSize请求 Q_UNUSED(requestedSize); diff --git a/src/appdata/app-icon-provider.h b/src/appdata/app-icon-provider.h index 51100ac..dafd587 100644 --- a/src/appdata/app-icon-provider.h +++ b/src/appdata/app-icon-provider.h @@ -30,6 +30,7 @@ class AppIconProvider : public QQuickImageProvider public: AppIconProvider(); QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override; + static QPixmap getPixmap(const QString &id, QSize *size, const QSize &requestedSize); private: static void loadPixmap(const QString &id, QPixmap &pixmap); diff --git a/src/items/theme-icon.cpp b/src/items/theme-icon.cpp new file mode 100644 index 0000000..0de8703 --- /dev/null +++ b/src/items/theme-icon.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2023, KylinSoft Co., Ltd. + * + * 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 + * (at your option) 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 . + * + * Authors: hxf + * + */ + +#include "theme-icon.h" +#include "settings.h" +#include "app-icon-provider.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define COLOR_DIFFERENCE 10 +using namespace UkuiMenu; + +QColor ThemeIcon::symbolicColor = QColor(31, 32, 34, 192); + +ThemeIcon::ThemeIcon(QQuickItem *parent) : QQuickPaintedItem(parent) +{ + connect(GlobalSetting::instance(), &GlobalSetting::styleChanged, this, [this] (const GlobalSetting::Key& key) { + if (key == GlobalSetting::StyleName) { + checkThemeName(); + } + }); +} + +void ThemeIcon::checkThemeName() +{ + if (!m_autoHighlight) { + return; + } + m_highLight = (GlobalSetting::instance()->get(GlobalSetting::StyleName).toString() != UKUI_STYLE_VALUE_LIGHT); + update(); +} + +void ThemeIcon::paint(QPainter *painter) +{ + //默认居中绘制 + QRect rect(0, 0, static_cast(width()), static_cast(height())); + + QPixmap pixmap = m_pixmap; + + painter->save(); + //抗锯齿,平滑过渡 + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + + if (m_disabled) { + QPainter p(&pixmap); + p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + p.setCompositionMode(QPainter::CompositionMode_SourceIn); + p.fillRect(pixmap.rect(), QGuiApplication::palette().color(QPalette::Disabled, QPalette::ButtonText)); + + } else if (m_highLight && (m_isPureColor || m_forceHighlight)) { + QPainter p(&pixmap); + p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + p.setCompositionMode(QPainter::CompositionMode_SourceIn); + p.fillRect(pixmap.rect(), QGuiApplication::palette().color(QPalette::HighlightedText)); + } + + if (m_radius > 0) { + int radius = qMin(m_radius, qMin((rect.height() / 2), (rect.width() / 2))); + QPainterPath path; + path.addRoundedRect(rect, radius, radius); + painter->setClipPath(path); + } + + painter->drawPixmap(rect, pixmap, pixmap.rect()); + painter->restore(); +} + +QString ThemeIcon::getSource() +{ + return m_source; +} + +void ThemeIcon::setSource(const QString &source) +{ + if (source.isEmpty()) { + qWarning() << "ThemeIcon: source is empty!"; + return; + } + + QString prefix("image://appicon/"); + m_source = source; + if (m_source.startsWith(prefix)) { + m_source = m_source.mid(prefix.size()); + } + + QSize size; + m_pixmap = UkuiMenu::AppIconProvider::getPixmap(m_source, &size, size); + if (m_pixmap.isNull()) { + return; + } + m_isPureColor = isPixmapPureColor(m_pixmap); + checkThemeName(); + update(); +} + +QString ThemeIcon::getFallBack() +{ + return m_fallback; +} + +void ThemeIcon::setFallBack(const QString &fallback) +{ + if (fallback.isEmpty()) { + qWarning() << "ThemeIcon: fallback is empty!"; + return; + } + + m_fallback = fallback; + if (m_pixmap.isNull()) { + setSource(fallback); + } +} + +bool ThemeIcon::isHighLight() const +{ + return m_highLight; +} + +void ThemeIcon::setHighLight(bool highLight) +{ +// qDebug() << "set high-light" << highLight; + m_highLight = highLight; + update(); +} + +bool ThemeIcon::isForceHighlight() const +{ + return m_forceHighlight; +} + +void ThemeIcon::setForceHighLight(bool force) +{ + m_forceHighlight = force; + update(); +} + +bool ThemeIcon::disable() const +{ + return m_disabled; +} + +void ThemeIcon::setDisable(bool disable) +{ + m_disabled = disable; + update(); +} + +//copy from ukui-platform-theme +bool ThemeIcon::isPixmapPureColor(const QPixmap &pixmap) +{ + if (pixmap.isNull()) { + qWarning("pixmap is null!"); + return false; + } + QImage image = pixmap.toImage(); + + QVector vector; + int total_red = 0; + int total_green = 0; + int total_blue = 0; + bool pure = true; + for (int y = 0; y < image.height(); ++y) { + for (int x = 0; x < image.width(); ++x) { + if (image.pixelColor(x, y).alphaF() > 0.3) { + QColor color = image.pixelColor(x, y); + vector << color; + total_red += color.red(); + total_green += color.green(); + total_blue += color.blue(); + int dr = qAbs(color.red() - symbolicColor.red()); + int dg = qAbs(color.green() - symbolicColor.green()); + int db = qAbs(color.blue() - symbolicColor.blue()); + if (dr > COLOR_DIFFERENCE || dg > COLOR_DIFFERENCE || db > COLOR_DIFFERENCE) + pure = false; + } + } + } + + if (pure) + return true; + + qreal squareRoot_red = 0; + qreal squareRoot_green = 0; + qreal squareRoot_blue = 0; + qreal average_red = total_red / vector.count(); + qreal average_green = total_green / vector.count(); + qreal average_blue = total_blue / vector.count(); + for (QColor color : vector) { + squareRoot_red += (color.red() - average_red) * (color.red() - average_red); + squareRoot_green += (color.green() - average_green) * (color.green() - average_green); + squareRoot_blue += (color.blue() - average_blue) * (color.blue() - average_blue); + } + + qreal arithmeticSquareRoot_red = qSqrt(squareRoot_red / vector.count()); + qreal arithmeticSquareRoot_green = qSqrt(squareRoot_green / vector.count()); + qreal arithmeticSquareRoot_blue = qSqrt(squareRoot_blue / vector.count()); + + return arithmeticSquareRoot_red < 2.0 && arithmeticSquareRoot_green < 2.0 && arithmeticSquareRoot_blue < 2.0; +} + +int ThemeIcon::radius() +{ + return m_radius; +} + +void ThemeIcon::setRadius(int radius) +{ + m_radius = radius < 0 ? 0 : radius; +} + +bool ThemeIcon::autoHighLight() const +{ + return m_autoHighlight; +} + +void ThemeIcon::setAutoHighLight(bool autoHighlight) +{ + m_autoHighlight = autoHighlight; + checkThemeName(); +} diff --git a/src/items/theme-icon.h b/src/items/theme-icon.h new file mode 100644 index 0000000..e1d68ea --- /dev/null +++ b/src/items/theme-icon.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023, KylinSoft Co., Ltd. + * + * 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 + * (at your option) 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 . + * + * Authors: hxf + * + */ + +#ifndef UKUI_MENU_THEME_ICON_H +#define UKUI_MENU_THEME_ICON_H + +#include +#include + +namespace UkuiMenu { + +class ThemeIcon : public QQuickPaintedItem +{ + Q_OBJECT + Q_PROPERTY(bool disable READ disable WRITE setDisable) + Q_PROPERTY(bool autoHighLight READ autoHighLight WRITE setAutoHighLight) + Q_PROPERTY(bool highLight READ isHighLight WRITE setHighLight) + Q_PROPERTY(bool forceHighlight READ isForceHighlight WRITE setForceHighLight) + Q_PROPERTY(int radius READ radius WRITE setRadius) + Q_PROPERTY(QString source READ getSource WRITE setSource) + Q_PROPERTY(QString fallback READ getFallBack WRITE setFallBack) + +public: + explicit ThemeIcon(QQuickItem *parent = nullptr); + + void paint(QPainter *painter) override; + QString getSource(); + void setSource(const QString &source); + + QString getFallBack(); + void setFallBack(const QString &fallback); + + bool isHighLight() const; + void setHighLight(bool highLight); + + bool autoHighLight() const; + void setAutoHighLight(bool autoHighlight); + + bool isForceHighlight() const; + void setForceHighLight(bool force); + + bool disable() const; + void setDisable(bool disable); + + int radius(); + void setRadius(int radius); + +private: + void checkThemeName(); + static bool isPixmapPureColor(const QPixmap &pixmap); + +private: + bool m_disabled = false; + bool m_highLight = false; + bool m_forceHighlight = false; + bool m_autoHighlight = true; + bool m_isPureColor = true; + int m_radius = 0; + QString m_source; + QString m_fallback; + QPixmap m_pixmap; + + static QColor symbolicColor; + +}; + +} + +#endif // UKUI_MENU_THEME_ICON_H diff --git a/src/ukui-menu-application.cpp b/src/ukui-menu-application.cpp index e6476eb..ef6a3e3 100644 --- a/src/ukui-menu-application.cpp +++ b/src/ukui-menu-application.cpp @@ -27,6 +27,7 @@ #include "extension/menu-extension.h" #include "app-page-header-utils.h" #include "power-button.h" +#include "items/theme-icon.h" #include #include @@ -61,6 +62,7 @@ void UkuiMenuApplication::registerQmlTypes() ModelManager::registerMetaTypes(); // vis colors + qmlRegisterType(uri, versionMajor, versionMinor, "ThemeIcon"); qRegisterMetaType("Palette::ColorRole"); qRegisterMetaType("Palette::ColorGroup"); qRegisterMetaType("ColorHelper::Role");