diff --git a/CMakeLists.txt b/CMakeLists.txt index c8c6bf70d..f34aefce4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ set(plasma_LIB_SRCS framesvg.cpp plasma.cpp popupapplet.cpp + framebackgroundprovider.cpp private/applethandle.cpp private/datacontainer_p.cpp private/desktoptoolbox.cpp @@ -223,6 +224,7 @@ set(plasma_LIB_INCLUDES servicejob.h svg.h theme.h + framebackgroundprovider.h tooltipcontent.h tooltipmanager.h tooltipmanager.h diff --git a/framebackgroundprovider.cpp b/framebackgroundprovider.cpp new file mode 100644 index 000000000..ec9eaa166 --- /dev/null +++ b/framebackgroundprovider.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2009 David Nolden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "framebackgroundprovider.h" + +namespace Plasma { + +FrameBackgroundProvider::~FrameBackgroundProvider() { +} + +} \ No newline at end of file diff --git a/framebackgroundprovider.h b/framebackgroundprovider.h new file mode 100644 index 000000000..6d0b7d3e6 --- /dev/null +++ b/framebackgroundprovider.h @@ -0,0 +1,56 @@ +/* + * Copyright 2009 David Nolden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PLASMA_PANELBACKGROUNDPROVIDER_H +#define PLASMA_PANELBACKGROUNDPROVIDER_H + +#include +#include + +class QPainter; +class QRegion; + +namespace Plasma { + +/** + * Abstract class to provide additional panel backgrounds behind translucent panels. + */ +class PLASMA_EXPORT FrameBackgroundProvider +{ +public: + virtual ~FrameBackgroundProvider(); + /** + * Returns an identity that can be used for caching the result of the background rendering. + * @return The identity string + */ + virtual QString identity() = 0; + + /** + * Applies the background to the given target. The target must have correct alpha-values, + * so the background can be painted under it. Also the clip-region must be already set correctly + * to restrict the area where the background is painted. + * @param target The target where the background should be painted + * @param offset Additional offset for the rendering: The render-source is translated by this offset + */ + virtual void apply(QPainter& target, QPoint offset = QPoint()) = 0; +}; + +} + +#endif // PLASMA_PANELBACKGROUNDPROVIDER_H diff --git a/framesvg.cpp b/framesvg.cpp index 3d4d62412..cab91ffe2 100644 --- a/framesvg.cpp +++ b/framesvg.cpp @@ -32,6 +32,7 @@ #include #include +#include "framebackgroundprovider.h" namespace Plasma { @@ -384,20 +385,28 @@ void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos) painter->drawPixmap(pos, frame->cachedBackground); } +QString FrameSvgPrivate::cacheId(const FrameData* frame) const +{ + Theme *theme = Theme::defaultTheme(); + FrameBackgroundProvider* backgroundProvider = theme->frameBackgroundProvider(q->imagePath()); + + + return QString::fromLatin1("%6_%5_%4_%3_%2_%1_"). + arg(frame->enabledBorders).arg(frame->frameSize.width()).arg(frame->frameSize.height()).arg(prefix) + .arg(q->imagePath()).arg(backgroundProvider ? backgroundProvider->identity() : QString()); +} + void FrameSvgPrivate::generateBackground(FrameData *frame) { if (!frame->cachedBackground.isNull()) { return; } - - QString id = QString::fromLatin1("%5_%4_%3_%2_%1_"). - arg(frame->enabledBorders).arg(frame->frameSize.width()).arg(frame->frameSize.height()).arg(prefix).arg(q->imagePath()); + QString id = cacheId(frame); Theme *theme = Theme::defaultTheme(); - if (theme->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull()) { + if (theme->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull()) return; - } //kDebug() << "generating background"; const int topWidth = q->elementSize(prefix + "top").width(); @@ -602,6 +611,13 @@ void FrameSvgPrivate::generateBackground(FrameData *frame) p.drawPixmap(overlayPos, overlay, QRect(overlayPos, overlaySize)); } + if(!prefix.startsWith("mask-")) { + if(FrameBackgroundProvider* backgroundProvider = theme->frameBackgroundProvider(q->imagePath())) { + p.setClipRegion(q->mask() ); + backgroundProvider->apply(p); + } + } + if (!framesToSave.contains(prefix)) { framesToSave.append(prefix); } @@ -617,8 +633,7 @@ void FrameSvgPrivate::scheduledCacheUpdate() FrameData *frame = frames[prefix]; framesToSave.removeAll(prefixToSave); - QString id = QString::fromLatin1("%5_%4_%3_%2_%1_"). - arg(frame->enabledBorders).arg(frame->frameSize.width()).arg(frame->frameSize.height()).arg(prefix).arg(q->imagePath()); + QString id = cacheId(frame); //kDebug()<<"Saving to cache frame"<imagePath(themePath); - if (path == newPath) { - return; + if (path != newPath) { + path = newPath; + //delete d->renderer; we're a KSharedPtr + eraseRenderer(); + + // check if new theme svg wants colorscheme applied + bool wasApplyColors = applyColors; + checkApplyColorHint(); + if (applyColors && !Theme::defaultTheme()->colorScheme()) { + if (!wasApplyColors) { + QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), + q, SLOT(colorsChanged())); + } + } else { + QObject::disconnect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), + q, SLOT(colorsChanged())); + } + localRectCache.clear(); + itemsToSave.clear(); + saveTimer->stop(); } - - path = newPath; - //delete d->renderer; we're a KSharedPtr - eraseRenderer(); - - // check if new theme svg wants colorscheme applied - bool wasApplyColors = applyColors; - checkApplyColorHint(); - if (applyColors && !Theme::defaultTheme()->colorScheme()) { - if (!wasApplyColors) { - QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), - q, SLOT(colorsChanged())); - } - } else { - QObject::disconnect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), - q, SLOT(colorsChanged())); - } - - localRectCache.clear(); - itemsToSave.clear(); - saveTimer->stop(); - + //Even trigger a repaint when the theme path has not changed. + //It may be other aspects of the theme rendering that changed (Composition type, background color, ...) //kDebug() << themePath << ">>>>>>>>>>>>>>>>>> theme changed"; emit q->repaintNeeded(); } diff --git a/theme.cpp b/theme.cpp index 4915c7342..10108bb5a 100644 --- a/theme.cpp +++ b/theme.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #ifdef Q_WS_X11 #include #endif @@ -40,6 +41,7 @@ #include #include "private/packages_p.h" +#include "framebackgroundprovider.h" namespace Plasma { @@ -49,6 +51,32 @@ namespace Plasma static const int DEFAULT_WALLPAPER_WIDTH = 1920; static const int DEFAULT_WALLPAPER_HEIGHT = 1200; +class StandardThemeBackgroundProvider : public FrameBackgroundProvider { + public: + StandardThemeBackgroundProvider(); + virtual void apply(QPainter& target, QPoint offset); + virtual QString identity(); + QColor m_color; + QString m_pattern; + int m_patternAlpha; + int m_offsetX; + int m_offsetY; + + void clearCache() { + m_cachedPatterns.clear(); + } + + private: + //Maps file-name to (image, alpha) + typedef QPair PatternAlphaPair; //The alpha value is statically applied to the pattern + QMap m_cachedPatterns; +}; + +static StandardThemeBackgroundProvider& standardThemeBackgroundProvider() { + static StandardThemeBackgroundProvider ret; + return ret; +} + class ThemePrivate { public: @@ -82,6 +110,10 @@ public: return KConfigGroup(KSharedConfig::openConfig(themeRcFile), "CachePolicies"); } + const KConfigGroup& config() const { + return const_cast(this)->config(); + } + KConfigGroup &config() { if (!cfg.isValid()) { @@ -102,6 +134,28 @@ public: return cfg; } + /** + * Reads optional configuration, that is specific to the current composite mode: + * When composition is active, the configuration entry is prefixed with "composite_". + * Optionally, the configuration can also be specific to the specified image path: + * Then the image path has to be appended to the configuration name + */ + template + T readOptionalConfig(QString configName, T _default, QString imagePath) { + if(compositingActive) + configName = "composite_" + configName; + + T ret = config().readEntry(configName, _default); + return config().readEntry(configName + "_" + imagePath, ret); + } + + bool hasOptionalConfig(QString configName, QString imagePath = QString()) { + if(compositingActive) + configName = "composite_" + configName; + + return config().hasKey(configName) || config().hasKey(configName + "_" + imagePath); + } + QString findInTheme(const QString &image, const QString &theme) const; void compositingChanged(); void discardCache(); @@ -164,7 +218,7 @@ QString ThemePrivate::findInTheme(const QString &image, const QString &theme) co if (locolor) { search = "desktoptheme/" + theme + "/locolor/" + image; search = KStandardDirs::locate("data", search); - } else if (!compositingActive) { + } else if (!compositingActive && !config().readEntry("forceTransparentTheme", false)) { search = "desktoptheme/" + theme + "/opaque/" + image; search = KStandardDirs::locate("data", search); } @@ -292,13 +346,26 @@ PackageStructure::Ptr Theme::packageStructure() void ThemePrivate::settingsFileChanged(const QString &file) { kDebug() << file; + QMap< QString, QString > oldEntries = config().entryMap(); + config().config()->reparseConfiguration(); - q->settingsChanged(); + + if(oldEntries != config().entryMap()) + q->settingsChanged(); } void Theme::settingsChanged() { - d->setThemeName(d->config().readEntry("name", ThemePrivate::defaultTheme), false); + standardThemeBackgroundProvider().clearCache(); //So we don't waste memory with background images that are not used + + QString newThemeName = d->config().readEntry("name", ThemePrivate::defaultTheme); + if(newThemeName != d->themeName) { + d->setThemeName(newThemeName, false); + }else{ + ///@todo More precise monitoring of attributes + d->discardCache(true); + emit themeChanged(); + } } void Theme::setThemeName(const QString &themeName) @@ -697,6 +764,86 @@ void Theme::setCacheLimit(int kbytes) } } +StandardThemeBackgroundProvider::StandardThemeBackgroundProvider() : m_color(Qt::black), m_patternAlpha(0) { +} + +void StandardThemeBackgroundProvider::apply(QPainter& target, QPoint offset) { + target.setCompositionMode(QPainter::CompositionMode_DestinationOver); + + //Apply color + if(m_color.alpha()) + target.fillRect(target.clipRegion().boundingRect(), m_color); + + //Apply pattern + if(m_patternAlpha && !m_pattern.isEmpty()) { + if(!m_cachedPatterns.contains(m_pattern) || m_cachedPatterns[m_pattern].second != m_patternAlpha) { + m_cachedPatterns.remove(m_pattern); + m_cachedPatterns.insert(m_pattern, PatternAlphaPair(QImage(m_pattern), m_patternAlpha)); + if(m_patternAlpha != 255) { + PatternAlphaPair& cached(m_cachedPatterns[m_pattern]); + //Apply lower alpha value to the pattern + QImage alpha(cached.first.size(), QImage::Format_ARGB32); + alpha.fill(QColor(cached.second, cached.second, cached.second).rgb()); + cached.first.setAlphaChannel(alpha); + } + } + + PatternAlphaPair& cached(m_cachedPatterns[m_pattern]); + + if(!cached.first.isNull()) { + QBrush brush; + QColor col(Qt::white); + col.setAlpha(m_patternAlpha); + brush.setColor(col); + brush.setTextureImage(cached.first); + target.setBrushOrigin(-(m_offsetX + offset.x()), -(m_offsetY + offset.y())); + target.fillRect(target.clipRegion().boundingRect(), brush); + }else{ + kDebug() << "failed to load pattern" << m_pattern; + } + } +} + +QString StandardThemeBackgroundProvider::identity() { + return QString("bgcolor_%1=").arg(m_color.alpha()) + m_color.name()+QString("_pattern_%1=").arg(m_patternAlpha)+m_pattern + QString("_offsets_%1_%2__").arg(m_offsetX).arg(m_offsetY); +} + +FrameBackgroundProvider* Theme::frameBackgroundProvider(QString imagePath) const { + if(d->locolor) + return 0; + + if((imagePath.startsWith("widgets/panel-") || imagePath.startsWith("dialogs/")) && + (d->hasOptionalConfig("frameBackgroundColor") || d->hasOptionalConfig("frameBackgroundPattern"))) + { + StandardThemeBackgroundProvider& provider(standardThemeBackgroundProvider());; + + provider.m_color = d->readOptionalConfig("frameBackgroundColor", Qt::black, imagePath); + if(d->hasOptionalConfig("frameBackgroundColor")) + provider.m_color.setAlpha(d->readOptionalConfig("frameBackgroundColorAlpha", 255, imagePath)); + else + provider.m_color.setAlpha(0); + + provider.m_pattern = d->readOptionalConfig("frameBackgroundPattern", QString(), imagePath); + provider.m_patternAlpha = d->readOptionalConfig("frameBackgroundPatternAlpha", 255, imagePath); + provider.m_offsetX = d->readOptionalConfig("frameBackgroundPatternOffsetX", 0, imagePath); + provider.m_offsetY = d->readOptionalConfig("frameBackgroundPatternOffsetY", 0, imagePath); + int randomX = d->readOptionalConfig("frameBackgroundPatternOffsetRandomX", 0, imagePath); + int randomY = d->readOptionalConfig("frameBackgroundPatternOffsetRandomY", 0, imagePath); + + if(randomX || randomY) { + //Add "this" so the offsets are different after every startup, but stay same for the same image path + qsrand(qHash(imagePath) + ((size_t)this) + randomX + 11 * randomY); + if(randomX) + provider.m_offsetX += qrand() % randomX; + if(randomY) + provider.m_offsetY += qrand() % randomY; + } + + return &provider; + }else + return 0; +} + } #include diff --git a/theme.h b/theme.h index 0d15f94d3..ce5583c60 100644 --- a/theme.h +++ b/theme.h @@ -31,7 +31,7 @@ namespace Plasma { - +class FrameBackgroundProvider; class ThemePrivate; /** @@ -265,6 +265,18 @@ class PLASMA_EXPORT Theme : public QObject */ void releaseRectsCache(const QString &image); + /** + * Returns a frame background provider, that allows intelligently filling the + * background of the frame represented by the given image. + * + * The ownership stays with the theme object, and the background provider + * is only for immediate usage. + * + * @param imagePath image path identifying the frame + * @return the backgrond-provider or zero, depending on the image, settings, and composition mode + */ + FrameBackgroundProvider* frameBackgroundProvider(QString imagePath) const; + Q_SIGNALS: /** * Emitted when the user changes the theme. SVGs should be reloaded at