Initial import of Aurorae Kwin decoration theme engine.

This theme engine uses SVG files to theme the decoration like the themes for Plasma desktop shell.

svn path=/trunk/playground/artwork/aurorae/; revision=983819
This commit is contained in:
Martin Gräßlin 2009-06-19 09:18:07 +00:00
parent 19495c6cd0
commit 2d24bce243
22 changed files with 2113 additions and 0 deletions

1
clients/aurorae/AUTHORS Normal file
View file

@ -0,0 +1 @@
Martin Gräßlin kde [at] martin [minus] graesslin [dot] com Developer and Maintainer

View file

@ -0,0 +1,14 @@
project(aurorae)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
find_package(KDE4 4.2.92 REQUIRED)
find_package(KDE4Workspace REQUIRED)
include (KDE4Defaults)
include (MacroLibrary)
add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES})
add_subdirectory(src)
add_subdirectory(themes/example-deco)

6
clients/aurorae/README Normal file
View file

@ -0,0 +1,6 @@
Aurorae is a themeable window decoration for KWin.
It supports theme files consisting of several SVG files for decoration and buttons. Themes can be
installed and selected directly in the configuration module of KWin decorations.
Please have a look at theme-description on how to write a theme file.

3
clients/aurorae/TODO Normal file
View file

@ -0,0 +1,3 @@
* Button positions are not updated after theme change
* Delete themes from selection
* Get Hot New Stuff support

View file

@ -0,0 +1,24 @@
########### decoration ###############
set(kwin3_aurorae_PART_SRCS aurorae.cpp themeconfig.cpp)
kde4_add_plugin(kwin3_aurorae ${kwin3_aurorae_PART_SRCS})
target_link_libraries(kwin3_aurorae ${KDE4_KDEUI_LIBS} ${KDE4_PLASMA_LIBS} ${KDE4WORKSPACE_KDECORATIONS_LIBS})
install(TARGETS kwin3_aurorae DESTINATION ${PLUGIN_INSTALL_DIR} )
########### install files ###############
install( FILES aurorae.desktop DESTINATION ${DATA_INSTALL_DIR}/kwin )
########### config ###############
set(kwin_aurorae_config_PART_SRCS config/config.cpp themeconfig.cpp )
kde4_add_ui_files(kwin_aurorae_config_PART_SRCS config/config.ui)
kde4_add_plugin(kwin_aurorae_config ${kwin_aurorae_config_PART_SRCS})
target_link_libraries(kwin_aurorae_config ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS} ${KDE4_PLASMA_LIBS} ${QT_QTGUI_LIBRARY})
install(TARGETS kwin_aurorae_config DESTINATION ${PLUGIN_INSTALL_DIR} )

View file

@ -0,0 +1,714 @@
/********************************************************************
Copyright (C) 2009 Martin Gräßlin <kde@martin-graesslin.com>
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 2 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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "aurorae.h"
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <KConfig>
#include <KConfigGroup>
#include <KDE/Plasma/Animator>
#include <KDE/Plasma/PaintUtils>
#include <KIconEffect>
#include <KIconLoader>
#include <QPainter>
namespace Aurorae
{
AuroraeFactory::AuroraeFactory()
: QObject()
, KDecorationFactoryUnstable()
{
init();
}
void AuroraeFactory::init()
{
KConfig conf("auroraerc");
KConfigGroup group(&conf, "Engine");
m_themeName = group.readEntry("ThemeName", "example-deco");
QString file("aurorae/themes/" + m_themeName + "/decoration.svg");
QString path = KGlobal::dirs()->findResource("data", file);
if (path.isEmpty()) {
file += "z";
path = KGlobal::dirs()->findResource("data", file);
}
if (path.isEmpty()) {
kDebug(1216) << "Could not find decoration svg: aborting";
abort();
}
m_frame.setImagePath(path);
m_frame.setCacheAllRenderedFrames(true);
m_frame.setEnabledBorders(Plasma::FrameSvg::AllBorders);
// load the buttons
initButtonFrame("minimize");
initButtonFrame("maximize");
initButtonFrame("restore");
initButtonFrame("close");
initButtonFrame("alldesktops");
initButtonFrame("keepabove");
initButtonFrame("keepbelow");
initButtonFrame("shade");
initButtonFrame("help");
readThemeConfig();
}
void AuroraeFactory::readThemeConfig()
{
// read config values
KConfig conf("aurorae/themes/" + m_themeName + "/" + m_themeName + "rc", KConfig::FullConfig, "data");
m_themeConfig.load(&conf);
}
AuroraeFactory::~AuroraeFactory()
{
s_instance = NULL;
}
AuroraeFactory *AuroraeFactory::instance()
{
if (!s_instance) {
s_instance = new AuroraeFactory;
}
return s_instance;
}
bool AuroraeFactory::reset(unsigned long changed)
{
// re-read config
m_frame.clearCache();
m_buttons.clear();
init();
resetDecorations(changed);
return false; // need hard reset
}
bool AuroraeFactory::supports(Ability ability) const
{
switch (ability) {
case AbilityAnnounceButtons:
case AbilityUsesAlphaChannel:
case AbilityButtonMenu:
case AbilityButtonSpacer:
return true;
case AbilityButtonMinimize:
return m_buttons.contains("minimize");
case AbilityButtonMaximize:
return m_buttons.contains("maximize") || m_buttons.contains("restore");
case AbilityButtonClose:
return m_buttons.contains("close");
case AbilityButtonAboveOthers:
return m_buttons.contains("keepabove");
case AbilityButtonBelowOthers:
return m_buttons.contains("keepbelow");
case AbilityButtonShade:
return m_buttons.contains("shade");
case AbilityButtonOnAllDesktops:
return m_buttons.contains("alldesktops");
case AbilityButtonHelp:
return m_buttons.contains("help");
case AbilityProvidesShadow:
return m_themeConfig.shadow();
default:
return false;
}
}
KDecoration *AuroraeFactory::createDecoration(KDecorationBridge *bridge)
{
AuroraeClient *client = new AuroraeClient(bridge, this);
return client->decoration();
}
void AuroraeFactory::initButtonFrame(const QString &button)
{
QString file("aurorae/themes/" + m_themeName + "/" + button + ".svg");
QString path = KGlobal::dirs()->findResource("data", file);
if (path.isEmpty()) {
// let's look for svgz
file.append("z");
path = KGlobal::dirs()->findResource("data", file);
}
if (!path.isEmpty()) {
Plasma::FrameSvg *frame = new Plasma::FrameSvg(this);
frame->setImagePath(path);
frame->setCacheAllRenderedFrames(true);
frame->setEnabledBorders(Plasma::FrameSvg::NoBorder);
m_buttons[ button ] = frame;
} else {
kDebug(1216) << "No button for: " << button;
}
}
Plasma::FrameSvg *AuroraeFactory::button(const QString &b)
{
if (hasButton(b)) {
return m_buttons[ b ];
} else {
return NULL;
}
}
AuroraeFactory *AuroraeFactory::s_instance = NULL;
/*******************************************************
* Button
*******************************************************/
AuroraeButton::AuroraeButton(ButtonType type, KCommonDecoration *parent)
: KCommonDecorationButton(type, parent)
, m_animationId(0)
, m_animationProgress(0.0)
, m_pressed(false)
{
setAttribute(Qt::WA_NoSystemBackground);
connect(Plasma::Animator::self(), SIGNAL(customAnimationFinished(int)), this, SLOT(animationFinished(int)));
}
void AuroraeButton::reset(unsigned long changed)
{
Q_UNUSED(changed)
}
void AuroraeButton::enterEvent(QEvent *event)
{
Q_UNUSED(event)
if (m_animationId != 0) {
Plasma::Animator::self()->stopCustomAnimation(m_animationId);
}
m_animationProgress = 0.0;
int time = AuroraeFactory::instance()->themeConfig().animationTime();
if (time != 0) {
m_animationId = Plasma::Animator::self()->customAnimation(40 / (1000.0 / qreal(time)),
time, Plasma::Animator::EaseInCurve, this, "animationUpdate");
}
update();
}
void AuroraeButton::leaveEvent(QEvent *event)
{
Q_UNUSED(event)
if (m_animationId != 0) {
Plasma::Animator::self()->stopCustomAnimation(m_animationId);
}
m_animationProgress = 0.0;
int time = AuroraeFactory::instance()->themeConfig().animationTime();
if (time != 0) {
m_animationId = Plasma::Animator::self()->customAnimation(40 / (1000.0 / qreal(time)),
time, Plasma::Animator::EaseOutCurve, this, "animationUpdate");
}
update();
}
void AuroraeButton::mousePressEvent(QMouseEvent *e)
{
m_pressed = true;
update();
KCommonDecorationButton::mousePressEvent(e);
}
void AuroraeButton::mouseReleaseEvent(QMouseEvent *e)
{
m_pressed = false;
update();
KCommonDecorationButton::mouseReleaseEvent(e);
}
void AuroraeButton::animationUpdate(double progress, int id)
{
Q_UNUSED(id)
m_animationProgress = progress;
update();
}
void AuroraeButton::animationFinished(int id)
{
if (m_animationId == id) {
m_animationId = 0;
update();
}
}
void AuroraeButton::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
if (decoration()->isPreview()) {
return;
}
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
bool active = decoration()->isActive();
if (type() == MenuButton) {
const QIcon icon = decoration()->icon();
const QSize size = icon.actualSize(QSize(16, 16));
QPixmap iconPix = icon.pixmap(size);
KIconEffect *effect = KIconLoader::global()->iconEffect();
if (active) {
if (underMouse()) {
iconPix = effect->apply(iconPix, KIconLoader::Desktop, KIconLoader::ActiveState);
}
} else {
iconPix = effect->apply(iconPix, KIconLoader::Desktop, KIconLoader::DisabledState);
}
painter.drawPixmap(0, 0, iconPix);
return;
}
ButtonStates states;
if (active) {
states |= Active;
}
if (underMouse()) {
states |= Hover;
}
if (m_pressed) {
states |= Pressed;
}
QString buttonName = "";
switch (type()) {
case MinButton:
if (!decoration()->isMinimizable()) {
states |= Deactivated;
}
buttonName = "minimize";
break;
case CloseButton:
if (!decoration()->isCloseable()) {
states |= Deactivated;
}
buttonName = "close";
break;
case MaxButton: {
if (!decoration()->isMaximizable()) {
states |= Deactivated;
}
buttonName = "maximize";
if (decoration()->maximizeMode() == KDecorationDefines::MaximizeFull &&
!decoration()->options()->moveResizeMaximizedWindows()) {
buttonName = "restore";
if (!AuroraeFactory::instance()->hasButton(buttonName)) {
buttonName = "maximize";
}
}
break;
}
case OnAllDesktopsButton:
if (decoration()->isOnAllDesktops()) {
states |= Hover;
}
buttonName = "alldesktops";
break;
case AboveButton:
buttonName = "keepabove";
break;
case BelowButton:
buttonName = "keepbelow";
break;
case ShadeButton:
if (!decoration()->isShadeable()) {
states |= Deactivated;
}
if (decoration()->isShade()) {
states |= Hover;
}
buttonName = "shade";
break;
case HelpButton:
buttonName = "help";
break;
default:
buttonName = QString();
}
if (!buttonName.isEmpty()) {
if (AuroraeFactory::instance()->hasButton(buttonName)) {
Plasma::FrameSvg *button = AuroraeFactory::instance()->button(buttonName);
paintButton(painter, button, states);
}
}
}
void AuroraeButton::paintButton(QPainter &painter, Plasma::FrameSvg *frame, ButtonStates states)
{
QString prefix = "active";
QString animationPrefix = "active";
bool hasInactive = false;
// check for inactive prefix
if (!states.testFlag(Active) && frame->hasElementPrefix("inactive")) {
// we have inactive, so we use it
hasInactive = true;
prefix = "inactive";
animationPrefix = "inactive";
}
if (states.testFlag(Hover)) {
if (states.testFlag(Active)) {
if (frame->hasElementPrefix("hover")) {
prefix = "hover";
}
} else {
if (hasInactive) {
if (frame->hasElementPrefix("hover-inactive")) {
prefix = "hover-inactive";
}
} else {
if (frame->hasElementPrefix("hover")) {
prefix = "hover";
}
}
}
}
if (states.testFlag(Pressed)) {
if (states.testFlag(Active)) {
if (frame->hasElementPrefix("pressed")) {
prefix = "pressed";
}
} else {
if (hasInactive) {
if (frame->hasElementPrefix("pressed-inactive")) {
prefix = "pressed-inactive";
}
} else {
if (frame->hasElementPrefix("pressed")) {
prefix = "pressed";
}
}
}
}
if (states.testFlag(Deactivated)) {
if (states.testFlag(Active)) {
if (frame->hasElementPrefix("deactivated")) {
prefix = "deactivated";
}
} else {
if (hasInactive) {
if (frame->hasElementPrefix("deactivated-inactive")) {
prefix = "deactivated-inactive";
}
} else {
if (frame->hasElementPrefix("deactivated")) {
prefix = "deactivated";
}
}
}
}
frame->setElementPrefix(prefix);
frame->resizeFrame(size());
if (m_animationId != 0) {
// there is an animation so we have to use it
// the animation is definately a hover animation as currently nothing else is supported
if (!states.testFlag(Hover)) {
// only have to set for not hover state as animationPrefix is set to (in)active by default
if (states.testFlag(Active)) {
if (frame->hasElementPrefix("hover")) {
animationPrefix = "hover";
}
} else {
if (hasInactive) {
if (frame->hasElementPrefix("hover-inactive")) {
animationPrefix = "hover-inactive";
}
} else {
if (frame->hasElementPrefix("hover")) {
animationPrefix = "hover";
}
}
}
}
QPixmap target = frame->framePixmap();
frame->setElementPrefix(animationPrefix);
frame->resizeFrame(size());
QPixmap result = Plasma::PaintUtils::transition(frame->framePixmap(),
target, m_animationProgress);
painter.drawPixmap(rect(), result);
} else {
frame->paintFrame(&painter);
}
}
/*******************************************************
* Client
*******************************************************/
AuroraeClient::AuroraeClient(KDecorationBridge *bridge, KDecorationFactory *factory)
: KCommonDecorationUnstable(bridge, factory)
{
}
AuroraeClient::~AuroraeClient()
{
}
void AuroraeClient::init()
{
KCommonDecoration::init();
}
void AuroraeClient::reset(unsigned long changed)
{
widget()->update();
KCommonDecoration::reset(changed);
}
QString AuroraeClient::visibleName() const
{
return QString("Aurorae Theme Engine");
}
QString AuroraeClient::defaultButtonsLeft() const
{
return AuroraeFactory::instance()->themeConfig().defaultButtonsLeft();
}
QString AuroraeClient::defaultButtonsRight() const
{
return AuroraeFactory::instance()->themeConfig().defaultButtonsRight();
}
bool AuroraeClient::decorationBehaviour(DecorationBehaviour behavior) const
{
switch (behavior) {
case DB_MenuClose:
return true; // Close on double click
case DB_WindowMask:
case DB_ButtonHide:
return false;
default:
return false;
}
}
int AuroraeClient::layoutMetric(LayoutMetric lm, bool respectWindowState,
const KCommonDecorationButton *button) const
{
bool maximized = maximizeMode() == MaximizeFull &&
!options()->moveResizeMaximizedWindows();
const ThemeConfig &conf = AuroraeFactory::instance()->themeConfig();
switch (lm) {
case LM_BorderLeft:
return maximized && respectWindowState ? 0 : conf.borderLeft();
case LM_BorderRight:
return maximized && respectWindowState ? 0 : conf.borderRight();
case LM_BorderBottom:
return maximized && respectWindowState ? 0 : conf.borderBottom();
case LM_OuterPaddingLeft:
return conf.paddingLeft();
case LM_OuterPaddingRight:
return conf.paddingRight();
case LM_OuterPaddingTop:
return conf.paddingTop();
case LM_OuterPaddingBottom:
return conf.paddingBottom();
case LM_TitleEdgeLeft:
return conf.titleEdgeLeft();
case LM_TitleEdgeRight:
return conf.titleBorderRight();
case LM_TitleEdgeTop:
return conf.titleEdgeTop();
case LM_TitleEdgeBottom:
return conf.titleEdgeBottom();
case LM_ButtonMarginTop:
return conf.buttonMarginTop();
case LM_TitleBorderLeft:
return conf.titleBorderLeft();
case LM_TitleBorderRight:
return conf.titleBorderRight();
case LM_TitleHeight:
return conf.titleHeight();
case LM_ButtonWidth:
return conf.buttonWidth();
case LM_ButtonHeight:
return conf.buttonHeight();
case LM_ButtonSpacing:
return conf.buttonSpacing();
case LM_ExplicitButtonSpacer:
return conf.explicitButtonSpacer();
default:
return KCommonDecoration::layoutMetric(lm, respectWindowState, button);
}
}
KCommonDecorationButton *AuroraeClient::createButton(ButtonType type)
{
AuroraeFactory *factory = AuroraeFactory::instance();
switch (type) {
case MenuButton:
return new AuroraeButton(type, this);
case MinButton:
if (factory->hasButton("minimize")) {
return new AuroraeButton(type, this);
} else {
return NULL;
}
case MaxButton:
if (factory->hasButton("maximize") || factory->hasButton("restore")) {
return new AuroraeButton(type, this);
} else {
return NULL;
}
case CloseButton:
if (factory->hasButton("close")) {
return new AuroraeButton(type, this);
} else {
return NULL;
}
case OnAllDesktopsButton:
if (factory->hasButton("alldesktops")) {
return new AuroraeButton(type, this);
} else {
return NULL;
}
case HelpButton:
if (factory->hasButton("help")) {
return new AuroraeButton(type, this);
} else {
return NULL;
}
case AboveButton:
if (factory->hasButton("keepabove")) {
return new AuroraeButton(type, this);
} else {
return NULL;
}
case BelowButton:
if (factory->hasButton("keepbelow")) {
return new AuroraeButton(type, this);
} else {
return NULL;
}
case ShadeButton:
if (factory->hasButton("shade")) {
return new AuroraeButton(type, this);
} else {
return NULL;
}
default:
return NULL;
}
}
void AuroraeClient::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
if (isPreview()) {
return;
}
bool maximized = maximizeMode() == MaximizeFull &&
!options()->moveResizeMaximizedWindows();
QPainter painter(widget());
painter.setCompositionMode(QPainter::CompositionMode_Source);
const ThemeConfig &conf = AuroraeFactory::instance()->themeConfig();
Plasma::FrameSvg *frame = AuroraeFactory::instance()->frame();
frame->setElementPrefix("decoration");
if (!isActive() && frame->hasElementPrefix("decoration-inactive")) {
frame->setElementPrefix("decoration-inactive");
}
if (!compositingActive() && frame->hasElementPrefix("decoration-opaque")) {
frame->setElementPrefix("decoration-opaque");
if (!isActive() && frame->hasElementPrefix("decoration-opaque-inactive")) {
frame->setElementPrefix("decoration-opaque-inactive");
}
}
// top
if (maximized) {
frame->setEnabledBorders(Plasma::FrameSvg::NoBorder);
} else {
frame->setEnabledBorders(Plasma::FrameSvg::AllBorders);
}
QRectF rect = QRectF(0.0, 0.0, widget()->width(), widget()->height());
QRectF sourceRect = rect;
if (!compositingActive()) {
rect = QRectF(conf.paddingLeft(), conf.paddingTop(),
widget()->width()-conf.paddingRight()-conf.paddingLeft(),
widget()->height()-conf.paddingBottom()-conf.paddingTop());
sourceRect = QRectF(0.0, 0.0, rect.width(), rect.height());
}
frame->resizeFrame(rect.size());
frame->paintFrame(&painter, rect, sourceRect);
if (isActive()) {
painter.setPen(conf.activeTextColor());
} else {
painter.setPen(conf.inactiveTextColor());
}
painter.setFont(options()->font(isActive()));
painter.drawText(titleRect(), conf.alignment() | conf.verticalAlignment() | Qt::TextSingleLine,
caption());
}
void AuroraeClient::updateWindowShape()
{
bool maximized = maximizeMode()==KDecorationDefines::MaximizeFull && !options()->moveResizeMaximizedWindows();
int w=widget()->width();
int h=widget()->height();
if (maximized) {
QRegion mask(0,0,w,h);
setMask(mask);
return;
}
const ThemeConfig &conf = AuroraeFactory::instance()->themeConfig();
Plasma::FrameSvg *deco = AuroraeFactory::instance()->frame();
if (!deco->hasElementPrefix("decoration-opaque")) {
// opaque element is missing: set generic mask
QRegion mask(0,0,w,h);
setMask(mask);
return;
}
deco->setElementPrefix("decoration-opaque");
deco->resizeFrame(QSize(w-conf.paddingLeft()-conf.paddingRight(),
h-conf.paddingTop()-conf.paddingBottom()));
QRegion mask = deco->mask().translated(conf.paddingLeft(), conf.paddingTop());
setMask(mask);
}
} // namespace Aurorae
extern "C"
{
KDE_EXPORT KDecorationFactory *create_factory() {
return Aurorae::AuroraeFactory::instance();
}
}
#include "aurorae.moc"

View file

@ -0,0 +1,3 @@
[Desktop Entry]
Name=Aurorae Decoration Theme Engine
X-KDE-Library=kwin3_aurorae

View file

@ -0,0 +1,132 @@
/********************************************************************
Copyright (C) 2009 Martin Gräßlin <kde@martin-graesslin.com>
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 2 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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef AURORAE_H
#define AURORAE_H
#include "themeconfig.h"
#include <kdecorationfactory.h>
#include <kcommondecoration.h>
#include <KDE/Plasma/FrameSvg>
class KComponentData;
namespace Aurorae
{
class AuroraeFactory : public QObject, public KDecorationFactoryUnstable
{
public:
~AuroraeFactory();
static AuroraeFactory* instance();
bool reset(unsigned long changed);
KDecoration *createDecoration(KDecorationBridge*);
bool supports(Ability ability) const;
Plasma::FrameSvg *frame() {
return &m_frame;
}
Plasma::FrameSvg *button(const QString& b);
bool hasButton(const QString& button) {
return m_buttons.contains(button);
}
ThemeConfig &themeConfig() {
return m_themeConfig;
}
private:
AuroraeFactory();
void init();
void initButtonFrame(const QString& button);
void readThemeConfig();
private:
static AuroraeFactory *s_instance;
// theme name
QString m_themeName;
ThemeConfig m_themeConfig;
// deco
Plasma::FrameSvg m_frame;
// buttons
QHash< QString, Plasma::FrameSvg* > m_buttons;
};
class AuroraeButton : public KCommonDecorationButton
{
Q_OBJECT
public:
AuroraeButton(ButtonType type, KCommonDecoration *parent);
void reset(unsigned long changed);
void enterEvent(QEvent *event);
void leaveEvent(QEvent *event);
void paintEvent(QPaintEvent *event);
public slots:
void animationUpdate(qreal progress, int id);
void animationFinished(int id);
protected:
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
private:
enum ButtonState {
Active = 0x1,
Hover = 0x2,
Pressed = 0x4,
Deactivated = 0x8
};
Q_DECLARE_FLAGS(ButtonStates, ButtonState);
void paintButton(QPainter& painter, Plasma::FrameSvg* frame, ButtonStates states);
private:
int m_animationId;
qreal m_animationProgress;
bool m_pressed;
};
class AuroraeClient : public KCommonDecorationUnstable
{
public:
AuroraeClient(KDecorationBridge *bridge, KDecorationFactory *factory);
~AuroraeClient();
virtual void init();
virtual QString visibleName() const;
virtual QString defaultButtonsLeft() const;
virtual QString defaultButtonsRight() const;
virtual bool decorationBehaviour(DecorationBehaviour behaviour) const;
virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true,
const KCommonDecorationButton * = 0) const;
virtual KCommonDecorationButton *createButton(ButtonType type);
virtual void updateWindowShape();
protected:
void reset(unsigned long changed);
void paintEvent(QPaintEvent *event);
};
}
#endif

View file

@ -0,0 +1,570 @@
/********************************************************************
Copyright (C) 2009 Martin Gräßlin <kde@martin-graesslin.com>
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 2 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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "config.h"
#include "themeconfig.h"
#include <klocalizedstring.h>
#include <KAboutApplicationDialog>
#include <KAboutData>
#include <KColorScheme>
#include <KConfig>
#include <KConfigGroup>
#include <KDesktopFile>
#include <KGlobal>
#include <KIO/NetAccess>
#include <KMessageBox>
#include <KStandardDirs>
#include <KTar>
#include <KUrlRequesterDialog>
#include <KDE/Plasma/FrameSvg>
#include <QAbstractItemView>
#include <QFile>
#include <QPainter>
extern "C"
{
KDE_EXPORT QObject *allocate_config(KConfig *conf, QWidget *parent)
{
return (new Aurorae::AuroraeConfig(conf, parent));
}
}
namespace Aurorae
{
//Theme selector code by Andre Duffeck (modified to add package description)
ThemeModel::ThemeModel(QObject *parent)
: QAbstractListModel(parent)
{
reload();
}
ThemeModel::~ThemeModel()
{
clearThemeList();
}
void ThemeModel::clearThemeList()
{
foreach(const ThemeInfo &themeInfo, m_themes) {
delete themeInfo.svg;
}
m_themes.clear();
}
void ThemeModel::reload()
{
reset();
clearThemeList();
// get all desktop themes
QStringList themes = KGlobal::dirs()->findAllResources("data",
"aurorae/themes/*/metadata.desktop",
KStandardDirs::NoDuplicates);
foreach(const QString &theme, themes) {
int themeSepIndex = theme.lastIndexOf('/', -1);
QString themeRoot = theme.left(themeSepIndex);
int themeNameSepIndex = themeRoot.lastIndexOf('/', -1);
QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1);
KDesktopFile df(theme);
QString name = df.readName();
if (name.isEmpty()) {
name = packageName;
}
QString comment = df.readComment();
QString author = df.desktopGroup().readEntry("X-KDE-PluginInfo-Author", QString());
QString email = df.desktopGroup().readEntry("X-KDE-PluginInfo-Email", QString());
QString version = df.desktopGroup().readEntry("X-KDE-PluginInfo-Version", QString());
QString license = df.desktopGroup().readEntry("X-KDE-PluginInfo-License", QString());
QString website = df.desktopGroup().readEntry("X-KDE-PluginInfo-Website", QString());
Plasma::FrameSvg *svg = new Plasma::FrameSvg(this);
QString svgFile = themeRoot + "/decoration.svg";
if (QFile::exists(svgFile)) {
svg->setImagePath(svgFile);
} else {
svg->setImagePath(svgFile + "z");
}
svg->setEnabledBorders(Plasma::FrameSvg::AllBorders);
ThemeConfig *config = new ThemeConfig();
KConfig conf("aurorae/themes/" + packageName + "/" + packageName + "rc", KConfig::FullConfig, "data");
config->load(&conf);
// buttons
QHash<QString, Plasma::FrameSvg*> *buttons = new QHash<QString, Plasma::FrameSvg*>();
initButtonFrame("minimize", packageName, buttons);
initButtonFrame("maximize", packageName, buttons);
initButtonFrame("restore", packageName, buttons);
initButtonFrame("close", packageName, buttons);
initButtonFrame("alldesktops", packageName, buttons);
initButtonFrame("keepabove", packageName, buttons);
initButtonFrame("keepbelow", packageName, buttons);
initButtonFrame("shade", packageName, buttons);
initButtonFrame("help", packageName, buttons);
ThemeInfo info;
info.package = packageName;
info.description = comment;
info.author = author;
info.email = email;
info.version = version;
info.website = website;
info.license = license;
info.svg = svg;
info.themeRoot = themeRoot;
info.themeConfig = config;
info.buttons = buttons;
m_themes[name] = info;
}
beginInsertRows(QModelIndex(), 0, m_themes.size());
endInsertRows();
}
int ThemeModel::rowCount(const QModelIndex &) const
{
return m_themes.size();
}
QVariant ThemeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (index.row() >= m_themes.size()) {
return QVariant();
}
QMap<QString, ThemeInfo>::const_iterator it = m_themes.constBegin();
for (int i = 0; i < index.row(); ++i) {
++it;
}
switch (role) {
case Qt::DisplayRole:
return it.key();
case PackageNameRole:
return (*it).package;
case SvgRole:
return qVariantFromValue((void*)(*it).svg);
case PackageDescriptionRole:
return (*it).description;
case PackageAuthorRole:
return (*it).author;
case PackageVersionRole:
return (*it).version;
case PackageEmailRole:
return (*it).email;
case PackageLicenseRole:
return (*it).license;
case PackageWebsiteRole:
return (*it).website;
case ThemeConfigRole:
return qVariantFromValue((void*)(*it).themeConfig);
case ButtonsRole:
return qVariantFromValue((void*)(*it).buttons);
default:
return QVariant();
}
}
int ThemeModel::indexOf(const QString &name) const
{
QMapIterator<QString, ThemeInfo> it(m_themes);
int i = -1;
while (it.hasNext()) {
++i;
if (it.next().value().package == name) {
return i;
}
}
return -1;
}
void ThemeModel::initButtonFrame(const QString &button, const QString &themeName, QHash<QString, Plasma::FrameSvg*> *buttons)
{
QString file("aurorae/themes/" + themeName + "/" + button + ".svg");
QString path = KGlobal::dirs()->findResource("data", file);
if (path.isEmpty()) {
// let's look for svgz
file.append("z");
path = KGlobal::dirs()->findResource("data", file);
}
if (!path.isEmpty()) {
Plasma::FrameSvg *frame = new Plasma::FrameSvg(this);
frame->setImagePath(path);
frame->setCacheAllRenderedFrames(true);
frame->setEnabledBorders(Plasma::FrameSvg::NoBorder);
buttons->insert(button, frame);
}
}
///////////////////////////////////////////////////
// ThemeDelegate
//////////////////////////////////////////////////
ThemeDelegate::ThemeDelegate(QObject* parent)
: QAbstractItemDelegate(parent)
{
}
void ThemeDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QString title = index.model()->data(index, Qt::DisplayRole).toString();
// highlight selected item
painter->save();
if (option.state & QStyle::State_Selected) {
painter->setBrush(option.palette.color(QPalette::Highlight));
} else {
painter->setBrush(Qt::gray);
}
painter->drawRect(option.rect);
painter->restore();
ThemeConfig *themeConfig = static_cast<ThemeConfig *>(
index.model()->data(index, ThemeModel::ThemeConfigRole).value<void *>());
painter->save();
paintDeco(painter, false, option, index, 5 + themeConfig->paddingLeft() + themeConfig->borderLeft(),
5, 5, 5 + themeConfig->paddingBottom() + themeConfig->borderBottom() );
painter->restore();
painter->save();
int activeLeft = 5;
int activeTop = 5 + themeConfig->paddingTop() + themeConfig->titleEdgeTop() +
themeConfig->titleEdgeBottom() + themeConfig->titleHeight();
int activeRight = 5 + themeConfig->paddingRight() + themeConfig->borderRight();
int activeBottom = 5;
paintDeco(painter, true, option, index, activeLeft, activeTop, activeRight, activeBottom);
painter->restore();
// paint title
painter->save();
QFont font = painter->font();
font.setWeight(QFont::Bold);
painter->setPen(themeConfig->activeTextColor());
painter->setFont(font);
painter->drawText(QRect(option.rect.topLeft() + QPoint(activeLeft, activeTop),
option.rect.bottomRight() - QPoint(activeRight, activeBottom)),
Qt::AlignCenter | Qt::TextWordWrap, title);
painter->restore();
}
void ThemeDelegate::paintDeco(QPainter *painter, bool active, const QStyleOptionViewItem &option, const QModelIndex &index,
int leftMargin, int topMargin,
int rightMargin, int bottomMargin) const
{
Plasma::FrameSvg *svg = static_cast<Plasma::FrameSvg *>(
index.model()->data(index, ThemeModel::SvgRole).value<void *>());
svg->setElementPrefix("decoration");
if (!active && svg->hasElementPrefix("decoration-inactive")) {
svg->setElementPrefix("decoration-inactive");
}
svg->resizeFrame(QSize(option.rect.width() - leftMargin - rightMargin, option.rect.height() - topMargin - bottomMargin));
svg->paintFrame(painter, option.rect.topLeft() + QPoint(leftMargin, topMargin));
ThemeConfig *themeConfig = static_cast<ThemeConfig *>(
index.model()->data(index, ThemeModel::ThemeConfigRole).value<void *>());
QHash<QString, Plasma::FrameSvg*> *buttons = static_cast<QHash<QString, Plasma::FrameSvg*> *>(
index.model()->data(index, ThemeModel::ButtonsRole).value<void *>());
int y = option.rect.top() + topMargin + themeConfig->paddingTop() + themeConfig->titleEdgeTop() + themeConfig->buttonMarginTop();
int x = option.rect.left() + leftMargin + themeConfig->paddingLeft() + themeConfig->titleEdgeLeft();
int buttonWidth = themeConfig->buttonWidth();
int buttonHeight = themeConfig->buttonHeight();
foreach (const QChar &character, themeConfig->defaultButtonsLeft()) {
QString buttonName;
if (character == '_'){
x += themeConfig->explicitButtonSpacer() + themeConfig->buttonSpacing();
continue;
}
else if (character == 'M') {
KIcon icon = KIcon( "xorg" );
QSize buttonSize(buttonWidth,buttonHeight);
painter->drawPixmap(QPoint(x,y), icon.pixmap(buttonSize));
x += buttonWidth;
}
else if (character == 'S') {
buttonName = "alldesktops";
}
else if (character == 'H') {
buttonName = "help";
}
else if (character == 'I') {
buttonName = "minimize";
}
else if (character == 'A') {
buttonName = "restore";
if (!buttons->contains(buttonName)) {
buttonName = "maximize";
}
}
else if (character == 'X') {
buttonName = "close";
}
else if (character == 'F') {
buttonName = "keepabove";
}
else if (character == 'B') {
buttonName = "keepbelow";
}
else if (character == 'L') {
buttonName = "shade";
}
if (!buttonName.isEmpty() && buttons->contains(buttonName)) {
Plasma::FrameSvg *frame = buttons->value(buttonName);
frame->setElementPrefix("active");
if (!active && frame->hasElementPrefix("inactive")) {
frame->setElementPrefix("inactive");
}
frame->resizeFrame(QSize(buttonWidth,buttonHeight));
frame->paintFrame(painter, QPoint(x, y));
x += buttonWidth;
}
x += themeConfig->buttonSpacing();
}
if (!themeConfig->defaultButtonsLeft().isEmpty()) {
x -= themeConfig->buttonSpacing();
}
int titleLeft = x;
x = option.rect.right() - rightMargin - themeConfig->paddingRight() - themeConfig->titleEdgeRight() - buttonWidth;
QString rightButtons;
foreach (const QChar &character, themeConfig->defaultButtonsRight()) {
rightButtons.prepend(character);
}
foreach (const QChar &character, rightButtons) {
QString buttonName;
if (character == '_'){
x -= themeConfig->explicitButtonSpacer() + themeConfig->buttonSpacing();
continue;
}
else if (character == 'M') {
KIcon icon = KIcon( "xorg" );
QSize buttonSize(buttonWidth,buttonHeight);
painter->drawPixmap(QPoint(x,y), icon.pixmap(buttonSize));
x -= buttonWidth;
}
else if (character == 'S') {
buttonName = "alldesktops";
}
else if (character == 'H') {
buttonName = "help";
}
else if (character == 'I') {
buttonName = "minimize";
}
else if (character == 'A') {
buttonName = "restore";
if (!buttons->contains(buttonName)) {
buttonName = "maximize";
}
}
else if (character == 'X') {
buttonName = "close";
}
else if (character == 'F') {
buttonName = "keepabove";
}
else if (character == 'B') {
buttonName = "keepbelow";
}
else if (character == 'L') {
buttonName = "shade";
}
if (!buttonName.isEmpty() && buttons->contains(buttonName)) {
Plasma::FrameSvg *frame = buttons->value(buttonName);
frame->setElementPrefix("active");
if (!active && frame->hasElementPrefix("inactive")) {
frame->setElementPrefix("inactive");
}
frame->resizeFrame(QSize(buttonWidth,buttonHeight));
frame->paintFrame(painter, QPoint(x, y));
x -= buttonWidth;
}
x -= themeConfig->buttonSpacing();
}
x += buttonWidth;
if (!rightButtons.isEmpty()){
x += themeConfig->buttonSpacing();
}
int titleRight = x;
// draw text
painter->save();
if (active) {
painter->setPen(themeConfig->activeTextColor());
}
else {
painter->setPen(themeConfig->inactiveTextColor());
}
y = option.rect.top() + topMargin + themeConfig->paddingTop() + themeConfig->titleEdgeTop();
QRectF titleRect(QPointF(titleLeft, y), QPointF(titleRight, y + themeConfig->titleHeight()));
QString caption = i18n("Active Window");
if (!active) {
caption = i18n("Inactive Window");
}
painter->drawText(titleRect,
themeConfig->alignment() | themeConfig->verticalAlignment() | Qt::TextSingleLine,
caption);
painter->restore();
}
QSize ThemeDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const
{
return QSize(400, 200);
}
///////////////////////////////////////////////////
// AuroraeConfig
//////////////////////////////////////////////////
AuroraeConfig::AuroraeConfig(KConfig* conf, QWidget* parent)
: QObject(parent)
, m_parent(parent)
{
Q_UNUSED(conf)
m_ui = new AuroraeConfigUI(parent);
m_ui->aboutPushButton->setIcon(KIcon("dialog-information"));
m_themeModel = new ThemeModel(this);
m_ui->theme->setModel(m_themeModel);
m_ui->theme->setItemDelegate(new ThemeDelegate(m_ui->theme->view()));
m_ui->theme->setMinimumSize(400, m_ui->theme->sizeHint().height());
m_config = new KConfig("auroraerc");
KConfigGroup group(m_config, "Engine");
load(group);
connect(m_ui->theme, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed()));
connect(m_ui->installNewThemeButton, SIGNAL(clicked(bool)), this, SLOT(slotInstallNewTheme()));
connect(m_ui->aboutPushButton, SIGNAL(clicked(bool)), this, SLOT(slotAboutClicked()));
m_ui->show();
}
AuroraeConfig::AuroraeConfig::~AuroraeConfig()
{
delete m_ui;
delete m_config;
}
void AuroraeConfig::defaults()
{
m_ui->theme->setCurrentIndex(m_themeModel->indexOf("example-deco"));
}
void AuroraeConfig::load(const KConfigGroup &conf)
{
QString theme = conf.readEntry("ThemeName", "example-deco");
m_ui->theme->setCurrentIndex(m_themeModel->indexOf(theme));
}
void AuroraeConfig::save(KConfigGroup &conf)
{
Q_UNUSED(conf)
KConfigGroup group(m_config, "Engine");
int index = m_ui->theme->currentIndex();
QString theme = m_ui->theme->itemData(index, ThemeModel::PackageNameRole).toString();
group.writeEntry("ThemeName", theme);
group.sync();
}
void AuroraeConfig::slotAboutClicked()
{
int index = m_ui->theme->currentIndex();
const QString name = m_ui->theme->itemData(index, Qt::DisplayRole).toString();
const QString comment = m_ui->theme->itemData(index, ThemeModel::PackageDescriptionRole).toString();
const QString author = m_ui->theme->itemData(index, ThemeModel::PackageAuthorRole).toString();
const QString email = m_ui->theme->itemData(index, ThemeModel::PackageEmailRole).toString();
const QString website = m_ui->theme->itemData(index, ThemeModel::PackageWebsiteRole).toString();
const QString version = m_ui->theme->itemData(index, ThemeModel::PackageVersionRole).toString();
const QString license = m_ui->theme->itemData(index, ThemeModel::PackageLicenseRole).toString();
KAboutData aboutData(name.toUtf8(), name.toUtf8(), ki18n(name.toUtf8()), version.toUtf8(), ki18n(comment.toUtf8()), KAboutLicense::byKeyword(license).key(), ki18n(QByteArray()), ki18n(QByteArray()), website.toLatin1());
aboutData.setProgramIconName("preferences-system-windows-action");
const QStringList authors = author.split(',');
const QStringList emails = email.split(',');
int i = 0;
if (authors.count() == emails.count()) {
foreach(const QString &author, authors) {
if (!author.isEmpty()) {
aboutData.addAuthor(ki18n(author.toUtf8()), ki18n(QByteArray()), emails[i].toUtf8(), 0);
}
i++;
}
}
KAboutApplicationDialog aboutPlugin(&aboutData, m_parent);
aboutPlugin.exec();
}
void AuroraeConfig::slotInstallNewTheme()
{
KUrl themeURL = KUrlRequesterDialog::getUrl(QString(), m_parent,
i18n("Drag or Type Theme URL"));
if (themeURL.url().isEmpty()) {
return;
}
// themeTmpFile contains the name of the downloaded file
QString themeTmpFile;
if (!KIO::NetAccess::download(themeURL, themeTmpFile, m_parent)) {
QString sorryText;
if (themeURL.isLocalFile()) {
sorryText = i18n("Unable to find the theme archive %1.", themeURL.prettyUrl());
}
else {
sorryText = i18n("Unable to download theme archive;\n"
"please check that address %1 is correct.", themeURL.prettyUrl());
}
KMessageBox::sorry(m_parent, sorryText);
return ;
}
// TODO: check if archive contains a valid theme
// TODO: show a progress dialog
KTar archive(themeTmpFile);
archive.open(QIODevice::ReadOnly);
const KArchiveDirectory* themeDir = archive.directory();
QString localThemesDir = KStandardDirs::locateLocal("data", "aurorae/themes/");
foreach(const QString& entry, themeDir->entries()) {
// entry has to be a directory to contain a theme
const KArchiveEntry* possibleEntry = themeDir->entry(entry);
if (possibleEntry->isDirectory()) {
const KArchiveDirectory* dir = dynamic_cast<const KArchiveDirectory*>(possibleEntry);
if (dir) {
dir->copyTo(localThemesDir + dir->name());
}
}
}
// and reload
int index = m_ui->theme->currentIndex();
const QString themeName = m_ui->theme->itemData(index, ThemeModel::PackageNameRole).toString();
m_themeModel->reload();
m_ui->theme->setCurrentIndex(m_themeModel->indexOf(themeName));
}
} // namespace
#include "config.moc"

View file

@ -0,0 +1,132 @@
/********************************************************************
Copyright (C) 2009 Martin Gräßlin <kde@martin-graesslin.com>
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 2 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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef AURORAE_CONFIG_H
#define AURORAE_CONFIG_H
#include <QWidget>
#include <QAbstractListModel>
#include <QAbstractItemDelegate>
#include <KConfig>
#include "ui_config.h"
namespace Plasma
{
class FrameSvg;
}
namespace Aurorae
{
class ThemeConfig;
//Theme selector code by Andre Duffeck (modified to add package description)
class ThemeInfo
{
public:
QString package;
Plasma::FrameSvg *svg;
QString description;
QString author;
QString email;
QString version;
QString website;
QString license;
QString themeRoot;
ThemeConfig *themeConfig;
QHash<QString, Plasma::FrameSvg*> *buttons;
};
class ThemeModel : public QAbstractListModel
{
public:
enum { PackageNameRole = Qt::UserRole,
SvgRole = Qt::UserRole + 1,
PackageDescriptionRole = Qt::UserRole + 2,
PackageAuthorRole = Qt::UserRole + 3,
PackageVersionRole = Qt::UserRole + 4,
PackageLicenseRole = Qt::UserRole + 5,
PackageEmailRole = Qt::UserRole + 6,
PackageWebsiteRole = Qt::UserRole + 7,
ThemeConfigRole = Qt::UserRole + 8,
ButtonsRole = Qt::UserRole + 9
};
ThemeModel(QObject *parent = 0);
virtual ~ThemeModel();
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
int indexOf(const QString &name) const;
void reload();
void clearThemeList();
void initButtonFrame(const QString &button, const QString &path, QHash<QString, Plasma::FrameSvg*> *buttons);
private:
QMap<QString, ThemeInfo> m_themes;
};
class ThemeDelegate : public QAbstractItemDelegate
{
public:
ThemeDelegate(QObject *parent = 0);
virtual void paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const;
virtual QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const;
private:
void paintDeco(QPainter *painter, bool active, const QStyleOptionViewItem &option, const QModelIndex &index,
int leftMargin, int topMargin,
int rightMargin, int bottomMargin) const;
};
class AuroraeConfigUI : public QWidget, public Ui::ConfigUI
{
public:
AuroraeConfigUI(QWidget *parent) : QWidget(parent)
{
setupUi(this);
}
};
class AuroraeConfig: public QObject
{
Q_OBJECT
public:
AuroraeConfig(KConfig *conf, QWidget *parent);
~AuroraeConfig();
signals:
void changed();
public slots:
void load(const KConfigGroup &conf);
void save(KConfigGroup &conf);
void defaults();
private slots:
void slotAboutClicked();
void slotInstallNewTheme();
private:
QWidget *m_parent;
ThemeModel *m_themeModel;
KConfig *m_config;
AuroraeConfigUI *m_ui;
};
} // namespace
#endif

View file

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigUI</class>
<widget class="QWidget" name="ConfigUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Theme:</string>
</property>
<property name="buddy">
<cstring>theme</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="theme"/>
</item>
<item>
<widget class="KPushButton" name="aboutPushButton"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="KPushButton" name="installNewThemeButton">
<property name="text">
<string>Install New Theme</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KPushButton</class>
<extends>QPushButton</extends>
<header>kpushbutton.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,85 @@
/********************************************************************
Copyright (C) 2009 Martin Gräßlin <kde@martin-graesslin.com>
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 2 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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "themeconfig.h"
#include <KConfig>
#include <KConfigGroup>
namespace Aurorae
{
ThemeConfig::ThemeConfig()
{
}
void ThemeConfig::load(KConfig *conf)
{
KConfigGroup general(conf, "General");
m_activeTextColor = general.readEntry("ActiveTextColor", QColor(Qt::black));
m_inactiveTextColor = general.readEntry("InactiveTextColor", QColor(Qt::black));
QString alignment = (general.readEntry("TitleAlignment", "Left")).toLower();
if (alignment == "left") {
m_alignment = Qt::AlignLeft;
}
else if (alignment == "center") {
m_alignment = Qt::AlignCenter;
}
else {
m_alignment = Qt::AlignRight;
}
alignment = (general.readEntry("TitleVerticalAlignment", "Center")).toLower();
if (alignment == "top") {
m_verticalAlignment = Qt::AlignTop;
}
else if (alignment == "center") {
m_verticalAlignment = Qt::AlignVCenter;
}
else {
m_verticalAlignment = Qt::AlignBottom;
}
m_animationTime = general.readEntry("Animation", 0);
m_defaultButtonsLeft = general.readEntry("LeftButtons", "MS");
m_defaultButtonsRight = general.readEntry("RightButtons", "HIA__X");
m_shadow = general.readEntry("Shadow", true);
KConfigGroup border(conf, "Layout");
// default values taken from KCommonDecoration::layoutMetric() in kcommondecoration.cpp
m_borderLeft = border.readEntry("BorderLeft", 5);
m_borderRight = border.readEntry("BorderRight", 5);
m_borderBottom = border.readEntry("BorderBottom", 5);
m_titleEdgeTop = border.readEntry("TitleEdgeTop", 5);
m_titleEdgeBottom = border.readEntry("TitleEdgeBottom", 5);
m_titleEdgeLeft = border.readEntry("TitleEdgeLeft", 5);
m_titleEdgeRight = border.readEntry("TitleEdgeRight", 5);
m_titleBorderLeft = border.readEntry("TitleBorderLeft", 5);
m_titleBorderRight = border.readEntry("TitleBorderRight", 5);
m_titleHeight = border.readEntry("TitleHeight", 20);
m_buttonWidth = border.readEntry("ButtonWidth", 20);
m_buttonHeight = border.readEntry("ButtonHeight", 20);
m_buttonSpacing = border.readEntry("ButtonSpacing", 5);
m_buttonMarginTop = border.readEntry("ButtonMarginTop", 0);
m_explicitButtonSpacer = border.readEntry("ExplicitButtonSpacer", 10);
m_paddingLeft = border.readEntry("PaddingLeft", 0);
m_paddingRight = border.readEntry("PaddingRight", 0);
m_paddingTop = border.readEntry("PaddingTop", 0);
m_paddingBottom = border.readEntry("PaddingBottom", 0);
}
} //namespace

View file

@ -0,0 +1,166 @@
/********************************************************************
Copyright (C) 2009 Martin Gräßlin <kde@martin-graesslin.com>
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 2 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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef THEMECONFIG_H
#define THEMECONFIG_H
// This class encapsulates all theme config values
// it's a seperate class as it's needed by both deco and config dialog
#include <QString>
#include <QColor>
class KConfig;
namespace Aurorae
{
class ThemeConfig
{
public:
ThemeConfig();
void load(KConfig *conf);
~ThemeConfig() {};
// active window
QColor activeTextColor() const {
return m_activeTextColor;
}
// inactive window
QColor inactiveTextColor() const {
return m_inactiveTextColor;
}
// Alignment
Qt::Alignment alignment() const {
return m_alignment;
};
Qt::Alignment verticalAlignment() const {
return m_verticalAlignment;
}
int animationTime() const {
return m_animationTime;
}
// Borders
int borderLeft() const {
return m_borderLeft;
}
int borderRight() const {
return m_borderRight;
}
int borderBottom() const {
return m_borderBottom;
}
int titleEdgeTop() const {
return m_titleEdgeTop;
}
int titleEdgeBottom() const {
return m_titleEdgeBottom;
}
int titleEdgeLeft() const {
return m_titleEdgeLeft;
}
int titleEdgeRight() const {
return m_titleEdgeRight;
}
int titleBorderLeft() const {
return m_titleBorderLeft;
}
int titleBorderRight() const {
return m_titleBorderRight;
}
int titleHeight() const {
return m_titleHeight;
}
int buttonWidth() const {
return m_buttonWidth;
}
int buttonHeight() const {
return m_buttonHeight;
}
int buttonSpacing() const {
return m_buttonSpacing;
}
int buttonMarginTop() const {
return m_buttonMarginTop;
}
int explicitButtonSpacer() const {
return m_explicitButtonSpacer;
}
int paddingLeft() const {
return m_paddingLeft;
}
int paddingRight() const {
return m_paddingRight;
}
int paddingTop() const {
return m_paddingTop;
}
int paddingBottom() const {
return m_paddingBottom;
}
QString defaultButtonsLeft() const {
return m_defaultButtonsLeft;
}
QString defaultButtonsRight() const {
return m_defaultButtonsRight;
}
bool shadow() const {
return m_shadow;
}
private:
QColor m_activeTextColor;
QColor m_inactiveTextColor;
Qt::Alignment m_alignment;
Qt::Alignment m_verticalAlignment;
// borders
int m_borderLeft;
int m_borderRight;
int m_borderBottom;
// title
int m_titleEdgeTop;
int m_titleEdgeBottom;
int m_titleEdgeLeft;
int m_titleEdgeRight;
int m_titleBorderLeft;
int m_titleBorderRight;
int m_titleHeight;
// buttons
int m_buttonWidth;
int m_buttonHeight;
int m_buttonSpacing;
int m_buttonMarginTop;
int m_explicitButtonSpacer;
// padding
int m_paddingLeft;
int m_paddingRight;
int m_paddingTop;
int m_paddingBottom;
int m_animationTime;
QString m_defaultButtonsLeft;
QString m_defaultButtonsRight;
bool m_shadow;
};
}
#endif

View file

@ -0,0 +1,122 @@
DESCRIPTION OF AURORAE
======================
Aurorae is a theme engine for KWin window decorations. It is built against the unstable API of KWin
in KDE 4.3. Aurorae uses SVG to render the decoration and buttons and there is a simple config file
for configuring the theme details.
This theme engine uses Plasma technologie to render the window decoration. Every detail can be
themed by the usage of SVG. The theme engine uses Plasma's FrameSvg, so you can provide SVG files
containing borders. This is described in more detail in techbase:
http://techbase.kde.org/Projects/Plasma/Theme#Backgrounds
The theme consists of one folder containing svgz files for decoration and buttons, one KConfig file
for the theme details and one metadata.desktop file which you can use to name your theme, author
information, etc.
Although the engine uses Plasma technology, it isn't Plasma. So it does not know anything about
Plasmoids and you will never be able to put Plasmoids into the decoration. That is out of scope of
this engine.
Aurorae uses the features provided by KWin 4.3. So the themes can provide their own decoration
shadows and it is recommended that your themes provide those. The engine supports ARGB decoration
which is enabled by default. If you provide a theme using translucency, please make sure, that it
works without compositing as well.
Window Decoration
=================
The window decoration has to be provided in file "decoration.svgz". This svg has to contain all the
elements required for a Plasma theme background. The decoration has to use the element prefix
"decoration".
If you want to provide a different style for inactive windows you can add it to the same svg. The
inactive elements must have the element prefix "decoration-inactive". The theme engine tests for
this prefix and if not provided inactive windows will be rendered with the same style as active
windows.
You have to provide a special decoration for opaque mode, that is when compositing is not active.
This opaque decoration is used for generating the window mask. The element prefix is
"decoration-opaque" for active and "decoration-opaque-inactive" for inactive windows. The mask is
generated from the active window.
Buttons
=======
You have to provide a svgz file for each button your theme should contain. If you do not provide a
file for a button type the engine will not include that button, so your decoration will miss it.
There is no fallback to a default theme. The buttons are rendered using Plasma's FrameSvg as well.
So you have to provide the "center" element. Borders are not supported
You can provide the following buttons:
* close
* minimize
* maximize
* restore
* alldesktops
* keepabove
* keepbelow
* shade
* resize
* help
Each button can have different states. So a button could be hovered, pressed, deactivated and you
might want to provide different styles for active and inactive windows. You can use the following
element prefix to provide styles for the buttons:
* active (normal button for active window)
* inactive (normal button for inactive window)
* hover (hover state for active window)
* hover-inactive (hover state for inactive window)
* pressed (button is pressed)
* pressed-inactive (pressed inactive button)
* deactivated (button cannot be clicked, e.g. window cannot be closed)
* deactivated-inactive (same for inactive windows)
You have at least to provide the active element. All other elements are optional and the active
element is always used as a fallback. If you provide the inactive element, this is used as a
fallback for the inactive window. That is, if you provide a hover element, but none for inactive,
the inactive window will not have a hover effect. Same is true for pressed and deactivated.
Reasonable that means if you provide a deactivated and an inactive element you want to provide a
deactivated-inactive element as well.
Configuration file
==================
The configuration file is a normal KConfig file. You have to give it the name of your decoration
with suffix "rc". So if your theme has the name "deco", your config file will be named "decorc".
The following section shows the possible options with their default values.
[General]
TitleAlignment=Left # vorizontal alignment of window title
TitleVerticalAlignment=Center # vertical alignment of window title
Animation=0 # animation duration in msec when hovering a button
ActiveTextColor=0,0,0,255 # title text color of active window
InactiveTextColor=0,0,0,255 # title text color of inactive window
LeftButtons=MS # buttons in left button group (see http://api.kde.org/4.x-api/kdebase-workspace-apidocs/kwin/lib/html/classKDecorationOptions.html#8ad12d76c93c5f1a12ea07b30f92d2fa)
RightButtons=HIA__X # buttons in right button group
Shadow=true # decoration provides shadows: you have to add padding
[Layout] # uses Layout Manager (see http://api.kde.org/4.x-api/kdebase-workspace-apidocs/kwin/lib/html/classKCommonDecoration.html#7932f74c28432ad8de232f1c6e8751ce)
BorderLeft=5
BorderRight=5
BorderBottom=5
TitleEdgeTop=5
TitleEdgeBottom=5
TitleEdgeLeft=5
TitleEdgeRight=5
TitleBorderLeft=5
TitleBorderRight=5
TitleHeight=20
ButtonWidth=20
ButtonHeight=20
ButtonSpacing=5
ButtonMarginTop=0
ExplicitButtonSpacer=10
PaddingTop=0 # Padding added to provide shadows
PaddingBottom=0 # Padding added to provide shadows
PaddingRight=0 # Padding added to provide shadows
PaddingLeft=0 # Padding added to provide shadows
Packaging
=========
All theme files (decoration, buttons, metadata.desktop and configuration file) have to be stored in
one directory with the name of the theme (this has to be identical to the one used for the config
file). You have to create a tar.gz archive from that directory. This archive is the theme, which
can be installed in the kcm for window decorations.

View file

@ -0,0 +1,4 @@
FILE(GLOB deco *.svgz)
install( FILES ${deco} example-decorc metadata.desktop DESTINATION ${DATA_INSTALL_DIR}/aurorae/themes/example-deco/ )

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,27 @@
[General]
ActiveTextColor=0,0,0,255
InactiveTextColor=178,178,178,178
TitleAlignment=Center
TitleVerticalAlignment=Bottom
Animation=200
[Layout]
BorderLeft=5
BorderRight=5
BorderBottom=5
TitleEdgeTop=5
TitleEdgeBottom=5
TitleEdgeLeft=5
TitleEdgeRight=5
TitleBorderLeft=5
TitleBorderRight=5
TitleHeight=20
ButtonWidth=20
ButtonHeight=20
ButtonSpacing=5
ButtonMarginTop=2
ExplicitButtonSpacer=10
PaddingTop=9
PaddingBottom=9
PaddingRight=9
PaddingLeft=9

Binary file not shown.

View file

@ -0,0 +1,12 @@
[Desktop Entry]
Name=Example
Comment=An example theme based on Air desktop theme
X-KDE-PluginInfo-Author=Martin Gräßlin
X-KDE-PluginInfo-Email=kde@martin-graesslin.com
X-KDE-PluginInfo-Name=example-deco
X-KDE-PluginInfo-Version=0.1
X-KDE-PluginInfo-Category=
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL_V2
X-KDE-PluginInfo-EnabledByDefault=true

Binary file not shown.

Binary file not shown.