diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ set(GRANTLEE_VERSION "5.1") find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Network) +find_package(KF5GuiAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5I18n ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5IconThemes ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5NewStuff ${KF5_MIN_VERSION} CONFIG REQUIRED) diff --git a/autotest/CMakeLists.txt b/autotest/CMakeLists.txt --- a/autotest/CMakeLists.txt +++ b/autotest/CMakeLists.txt @@ -1,7 +1,7 @@ add_definitions( -DGRANTLEETHEME_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) ecm_add_test(grantleethemetest.cpp NAME_PREFIX "grantleetheme-" - LINK_LIBRARIES Qt5::Test KF5::GrantleeTheme KF5::ConfigCore + LINK_LIBRARIES Qt5::Test KF5::GrantleeTheme KF5::ConfigCore Qt5::Gui ) ecm_add_test(grantleethememanagertest.cpp NAME_PREFIX "grantleetheme-" diff --git a/autotest/data/themes/color/color.html b/autotest/data/themes/color/color.html new file mode 100644 --- /dev/null +++ b/autotest/data/themes/color/color.html @@ -0,0 +1,21 @@ + +Color name: {{ pal.button|colorHexRgb }} +RGBA: {{ pal.button|colorCssRgba }} +Color lighter: {{ pal.button|colorLighter:150|colorHexRgb }} +Color darker: {{ pal.button|colorDarker:150|colorHexRgb }} + +Active: {{ pal.activeBase|colorHexRgb }} +Inactive: {{ pal.inactiveBase|colorHexRgb }} +Disabled: {{ pal.disabledBase|colorHexRgb }} +Default: {{ pal.base|colorHexRgb }} + +Red: {{ pal.button.red }} +Name: {{ pal.button.hexRgb }} +RGBA: {{ pal.button.cssRgba }} + +Mix literal/literal: {% colorMix "#ff0000" "#0000ff" 0.75 %} +Mix var/literal: {% colorMix pal.button "#0000ff" 0.5 %} +Mix var/var: {% colorMix pal.button pal.inactiveBase 0.25 %} + +Color literal: {{ "lightsteelblue"|colorHexRgb }} + diff --git a/autotest/data/themes/color/color.testdesktop b/autotest/data/themes/color/color.testdesktop new file mode 100644 --- /dev/null +++ b/autotest/data/themes/color/color.testdesktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Name=Color Unit Test +Description=Color Unit Test +FileName=color.html diff --git a/autotest/data/themes/color/color_expected.html b/autotest/data/themes/color/color_expected.html new file mode 100644 --- /dev/null +++ b/autotest/data/themes/color/color_expected.html @@ -0,0 +1,22 @@ + + +Color name: #ff0000 +RGBA: rgba(255, 0, 0, 1) +Color lighter: #ff7f7f +Color darker: #aa0000 + +Active: #ff0000 +Inactive: #00ff00 +Disabled: #0000ff +Default: #00ff00 + +Red: 255 +Name: #ff0000 +RGBA: rgba(255, 0, 0, 1) + +Mix literal/literal: rgba(64, 0, 191, 1) +Mix var/literal: rgba(128, 0, 128, 1) +Mix var/var: rgba(191, 64, 0, 1) + +Color literal: #b0c4de + diff --git a/autotest/grantleethemetest.cpp b/autotest/grantleethemetest.cpp --- a/autotest/grantleethemetest.cpp +++ b/autotest/grantleethemetest.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -173,6 +174,7 @@ QTest::newRow("valid theme") << QStringLiteral("valid") << true << QStringLiteral("filename.testdesktop") << QStringLiteral("header"); QTest::newRow("invalid theme") << QStringLiteral("invalid") << false << QStringLiteral("filename.testdesktop") << QString(); + QTest::newRow("color") << QStringLiteral("color") << true << QStringLiteral("color.testdesktop") << QStringLiteral("color"); } void GrantleeThemeTest::testRenderTemplate() @@ -191,6 +193,13 @@ data[QStringLiteral("title")] = QStringLiteral("Something's going on"); data[QStringLiteral("subtext")] = QStringLiteral("Please wait, it will be over soon."); + QPalette pal; + pal.setColor(QPalette::Button, Qt::red); + pal.setColor(QPalette::Active, QPalette::Base, Qt::red); + pal.setColor(QPalette::Inactive, QPalette::Base, Qt::green); + pal.setColor(QPalette::Disabled, QPalette::Base, Qt::blue); + data[QStringLiteral("pal")] = QVariant::fromValue(pal); + GrantleeTheme::Theme theme(themePath, dirname, filename); QCOMPARE(theme.isValid(), isValid); diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt --- a/src/plugin/CMakeLists.txt +++ b/src/plugin/CMakeLists.txt @@ -2,14 +2,17 @@ set(grantleeplugin_SRCS kdegrantleeplugin.cpp + color.cpp icon.cpp + palette.cpp ) add_library(kde_grantlee_plugin MODULE ${grantleeplugin_SRCS}) kpim_grantlee_adjust_plugin_name(kde_grantlee_plugin) target_link_libraries(kde_grantlee_plugin Grantlee5::Templates KF5::IconThemes + KF5::GuiAddons ) install(TARGETS kde_grantlee_plugin diff --git a/src/plugin/color.h b/src/plugin/color.h new file mode 100644 --- /dev/null +++ b/src/plugin/color.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019 Volker Krause + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GRANTLEETHEME_COLOR_H +#define GRANTLEETHEME_COLOR_H + +#include +#include + +/** + * @name colorHexRgb filter + * @brief Provides colorName filter for converting a QColor to a color string. + * + * The syntax is: + * @code + * {{ myColor|colorHexRgb }} + * @endcode + */ +class ColorHexRgbFilter : public Grantlee::Filter +{ +public: + QVariant doFilter(const QVariant &input, const QVariant &arg, bool autoescape) const override; +}; + +/** + * @name colorCssRgba filter + * @brief Provides colorRgba filter for converting a QColor to a rgba() CSS color definition. + * + * The syntax is: + * @code + * {{ myColor|colorCssRgba }} + * @endcode + */ +class ColorCssRgbaFilter : public Grantlee::Filter +{ +public: + QVariant doFilter(const QVariant &input, const QVariant &arg, bool autoescape) const override; +}; + +/** + * @name colorLighter filter + * @brief Provides colorLighter filter for applying QColor::lighter to a color. + * + * The syntax is: + * @code + * {{ myColor|colorLighter: }} + * @endcode + */ +class ColorLighterFilter : public Grantlee::Filter +{ +public: + QVariant doFilter(const QVariant &input, const QVariant &arg, bool autoescape) const override; +}; + +/** + * @name colorDarker filter + * @brief Provides colorDarker filter for applying QColor::darker to a color. + * + * The syntax is: + * @code + * {{ myColor|colorDarker: }} + * @endcode + */ +class ColorDarkerFilter : public Grantlee::Filter +{ +public: + QVariant doFilter(const QVariant &input, const QVariant &arg, bool autoescape) const override; +}; + +/** + * @name colorMix tag + * @brief Provides {% colorMix %} tag for mixing two colors + * + * The syntax is: + * @code + * {% colorMix "#rrggbb"|var-with-color "#rrggbb"|var-with-color %} + * @endcode + * + * The tag generates a rgba() CSS color definition. + */ +class ColorMixTag : public Grantlee::AbstractNodeFactory +{ + Q_OBJECT +public: + explicit ColorMixTag(QObject *parent = nullptr); + ~ColorMixTag(); + Grantlee::Node *getNode(const QString &tagContent, Grantlee::Parser *p) const override; +}; + +class ColorMixNode : public Grantlee::Node +{ + Q_OBJECT +public: + explicit ColorMixNode(const QString &color1Name, const QString &color2Name, double ratio, QObject *parent = nullptr); + ~ColorMixNode(); + void render(Grantlee::OutputStream *stream, Grantlee::Context *c) const override; + +private: + QString m_color1Name; + QString m_color2Name; + double m_ratio; +}; + +namespace Color +{ + void registerMetaType(); +} + +#endif // GRANTLEETHEME_COLOR_H diff --git a/src/plugin/color.cpp b/src/plugin/color.cpp new file mode 100644 --- /dev/null +++ b/src/plugin/color.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2019 Volker Krause + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "color.h" + +#include +#include +#include +#include + +#include +#include +#include + +static QColor inputToColor(const QVariant &v) +{ + if (v.canConvert()) { + return QColor(v.value().get()); + } + return v.value(); +} + +static QString rgbaString(const QColor &c) +{ + return QLatin1String("rgba(") + + QString::number(c.red()) + QLatin1String(", ") + + QString::number(c.green()) + QLatin1String(", ") + + QString::number(c.blue()) + QLatin1String(", ") + + QString::number(c.alphaF()) + QLatin1Char(')'); +} + +#define COLOR_PROP(name) if (property == QLatin1String(#name)) { return object.name(); } + +GRANTLEE_BEGIN_LOOKUP(QColor) + COLOR_PROP(red) + COLOR_PROP(green) + COLOR_PROP(blue) + COLOR_PROP(alpha) + if (property == QLatin1String("hexRgb")) { + return object.name(); + } + if (property == QLatin1String("cssRgba")) { + return rgbaString(object); + } + return {}; +GRANTLEE_END_LOOKUP + +void Color::registerMetaType() +{ + Grantlee::registerMetaType(); +} + +QVariant ColorHexRgbFilter::doFilter(const QVariant &input, const QVariant &arg, bool autoescape) const +{ + Q_UNUSED(arg) + Q_UNUSED(autoescape) + + const auto color = inputToColor(input); + return color.name(); +} + +QVariant ColorCssRgbaFilter::doFilter(const QVariant &input, const QVariant &arg, bool autoescape) const +{ + Q_UNUSED(arg) + Q_UNUSED(autoescape) + + const auto color = inputToColor(input); + return rgbaString(color); +} + +QVariant ColorLighterFilter::doFilter(const QVariant& input, const QVariant& arg, bool autoescape) const +{ + Q_UNUSED(autoescape) + + const auto color = inputToColor(input); + const auto factor = arg.toInt(); + return color.lighter(factor); +} + +QVariant ColorDarkerFilter::doFilter(const QVariant& input, const QVariant& arg, bool autoescape) const +{ + Q_UNUSED(autoescape) + + const auto color = inputToColor(input); + const auto factor = arg.toInt(); + return color.darker(factor); +} + + +ColorMixTag::ColorMixTag(QObject *parent) + : Grantlee::AbstractNodeFactory(parent) +{ +} + +ColorMixTag::~ColorMixTag() = default; + +Grantlee::Node* ColorMixTag::getNode(const QString &tagContent, Grantlee::Parser *p) const +{ + Q_UNUSED(p); + const auto parts = smartSplit(tagContent); + if (parts.size() != 4) { + throw Grantlee::Exception(Grantlee::TagSyntaxError, QStringLiteral("colormix tag needs 3 arguments")); + } + + return new ColorMixNode(parts.at(1), parts.at(2), parts.at(3).toDouble()); +} + +ColorMixNode::ColorMixNode(const QString &color1Name, const QString &color2Name, double ratio, QObject *parent) + : Grantlee::Node(parent) + , m_color1Name(color1Name) + , m_color2Name(color2Name) + , m_ratio(ratio) +{ +} + +ColorMixNode::~ColorMixNode() = default; + +static QColor resolveColor(const QString &name, Grantlee::Context *c) +{ + if (name.startsWith(QLatin1Char('"')) && name.endsWith(QLatin1Char('"'))) { + return QColor(name.midRef(1, name.size() - 2)); + } + + const auto val = Grantlee::Variable(name).resolve(c); + return val.value(); +} + +void ColorMixNode::render(Grantlee::OutputStream *stream, Grantlee::Context *c) const +{ + const auto col1 = resolveColor(m_color1Name, c); + const auto col2 = resolveColor(m_color2Name, c); + + (*stream) << rgbaString(KColorUtils::mix(col1, col2, m_ratio)); +} diff --git a/src/plugin/kdegrantleeplugin.cpp b/src/plugin/kdegrantleeplugin.cpp --- a/src/plugin/kdegrantleeplugin.cpp +++ b/src/plugin/kdegrantleeplugin.cpp @@ -18,12 +18,16 @@ */ #include "kdegrantleeplugin.h" +#include "color.h" #include "icon.h" +#include "palette.h" KDEGrantleePlugin::KDEGrantleePlugin(QObject *parent) : QObject(parent) , Grantlee::TagLibraryInterface() { + Color::registerMetaType(); + Palette::registerMetaType(); } KDEGrantleePlugin::~KDEGrantleePlugin() @@ -35,12 +39,19 @@ Q_UNUSED(name); QHash nodeFactories; + nodeFactories[QStringLiteral("colorMix")] = new ColorMixTag(); nodeFactories[QStringLiteral("icon")] = new IconTag(); return nodeFactories; } QHash KDEGrantleePlugin::filters(const QString &name) { - return Grantlee::TagLibraryInterface::filters(name); + Q_UNUSED(name) + QHash filters; + filters.insert(QStringLiteral("colorHexRgb"), new ColorHexRgbFilter()); + filters.insert(QStringLiteral("colorCssRgba"), new ColorCssRgbaFilter()); + filters.insert(QStringLiteral("colorLighter"), new ColorLighterFilter()); + filters.insert(QStringLiteral("colorDarker"), new ColorDarkerFilter()); + return filters; } diff --git a/src/plugin/kdegrantleeplugin.cpp b/src/plugin/palette.h copy from src/plugin/kdegrantleeplugin.cpp copy to src/plugin/palette.h --- a/src/plugin/kdegrantleeplugin.cpp +++ b/src/plugin/palette.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Daniel Vrátil + * Copyright (C) 2019 Volker Krause * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -14,33 +14,14 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * */ -#include "kdegrantleeplugin.h" -#include "icon.h" - -KDEGrantleePlugin::KDEGrantleePlugin(QObject *parent) - : QObject(parent) - , Grantlee::TagLibraryInterface() -{ -} +#ifndef GRANTLEETHEME_PALETTE_H +#define GRANTLEETHEME_PALETTE_H -KDEGrantleePlugin::~KDEGrantleePlugin() +namespace Palette { + void registerMetaType(); } -QHash KDEGrantleePlugin::nodeFactories(const QString &name) -{ - Q_UNUSED(name); - - QHash nodeFactories; - nodeFactories[QStringLiteral("icon")] = new IconTag(); - - return nodeFactories; -} - -QHash KDEGrantleePlugin::filters(const QString &name) -{ - return Grantlee::TagLibraryInterface::filters(name); -} +#endif // GRANTLEETHEME_PALETTE_H diff --git a/src/plugin/palette.cpp b/src/plugin/palette.cpp new file mode 100644 --- /dev/null +++ b/src/plugin/palette.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 Volker Krause + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "palette.h" + +#include + +#include + +#define ROLE(R) { #R, QPalette::R } + +static const struct { + const char* name; + QPalette::ColorRole role; +} color_roles[] = { + ROLE(AlternateBase), + ROLE(Base), + ROLE(BrightText), + ROLE(Button), + ROLE(ButtonText), + ROLE(Dark), + ROLE(Highlight), + ROLE(HighlightedText), + ROLE(Light), + ROLE(Link), + ROLE(LinkVisited), + ROLE(Mid), + ROLE(Midlight), + ROLE(PlaceholderText), + ROLE(Shadow), + ROLE(Text), + ROLE(ToolTipBase), + ROLE(ToolTipText), + ROLE(Window), + ROLE(WindowText), +}; + +GRANTLEE_BEGIN_LOOKUP(QPalette) + auto group = QPalette::Inactive; + auto roleName = property; + + if (property.startsWith(QLatin1String("active"))) { + roleName = property.mid(6); + group = QPalette::Active; + } else if (property.startsWith(QLatin1String("disabled"))) { + roleName = property.mid(8); + group = QPalette::Disabled; + } else if (property.startsWith(QLatin1String("inactive"))) { + roleName = property.mid(8); + group = QPalette::Inactive; + } + + for (const auto &role : color_roles) { + if (roleName.compare(QLatin1String(role.name), Qt::CaseInsensitive) == 0) { + return object.color(group, role.role); + } + } + return {}; +GRANTLEE_END_LOOKUP + +void Palette::registerMetaType() +{ + Grantlee::registerMetaType(); +}