- Allow painting a background color, or a background pattern behind freely floating elements of the theme, to provide a better fallback mechanism for transparent themes when composition is not available.

The color/pattern is given through the configuration file, and updating due to changes of the configuration file works perfectly. A simple user-interface to use this will be added to the panel configuration.
If you want to try this out, put into the [Theme] section of your plasmarc file:
frameBackgroundColor=#aacc00 (Your picked color)
frameBackgroundColorAlpha=120 (Alpha value for the color, between 0 and 255, 0=invisible, 255=opaque)
frameBackgroundPattern=/path/to/image
frameBackgroundPatternAlpha=255 (Alpha value for the pattern, as above)

The color is painted first, then the pattern. They are painted into the mask defined by the theme, so this only works nicely with themes that supply proper masks.


svn path=/trunk/KDE/kdelibs/; revision=961915
This commit is contained in:
David Nolden 2009-05-01 00:41:41 +00:00
parent f2ba67f0b1
commit 347c0caac4
8 changed files with 294 additions and 35 deletions

View File

@ -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

View File

@ -0,0 +1,27 @@
/*
* Copyright 2009 David Nolden <david.nolden.kdevelop@art-master.de>
*
* 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() {
}
}

56
framebackgroundprovider.h Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright 2009 David Nolden <david.nolden.kdevelop@art-master.de>
*
* 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 <plasma/plasma_export.h>
#include <QtCore/QPoint>
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

View File

@ -32,6 +32,7 @@
#include <plasma/theme.h>
#include <plasma/applet.h>
#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"<<id;

View File

@ -85,6 +85,8 @@ public:
frames.clear();
}
QString cacheId(const FrameData* frame) const;
void generateBackground(FrameData *frame);
void scheduledCacheUpdate();
void updateSizes();

46
svg.cpp
View File

@ -364,31 +364,29 @@ class SvgPrivate
QString newPath = Theme::defaultTheme()->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();
}

153
theme.cpp
View File

@ -22,6 +22,7 @@
#include <QApplication>
#include <QFile>
#include <QFileInfo>
#include <QtGui/QPainter>
#ifdef Q_WS_X11
#include <QX11Info>
#endif
@ -40,6 +41,7 @@
#include <kwindowsystem.h>
#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<QImage, uint> PatternAlphaPair; //The alpha value is statically applied to the pattern
QMap<QString, PatternAlphaPair > 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<ThemePrivate*>(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<class T>
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<bool>("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<QColor>("frameBackgroundColor", Qt::black, imagePath);
if(d->hasOptionalConfig("frameBackgroundColor"))
provider.m_color.setAlpha(d->readOptionalConfig<int>("frameBackgroundColorAlpha", 255, imagePath));
else
provider.m_color.setAlpha(0);
provider.m_pattern = d->readOptionalConfig<QString>("frameBackgroundPattern", QString(), imagePath);
provider.m_patternAlpha = d->readOptionalConfig<int>("frameBackgroundPatternAlpha", 255, imagePath);
provider.m_offsetX = d->readOptionalConfig<int>("frameBackgroundPatternOffsetX", 0, imagePath);
provider.m_offsetY = d->readOptionalConfig<int>("frameBackgroundPatternOffsetY", 0, imagePath);
int randomX = d->readOptionalConfig<int>("frameBackgroundPatternOffsetRandomX", 0, imagePath);
int randomY = d->readOptionalConfig<int>("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 <theme.moc>

14
theme.h
View File

@ -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