[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    User-provided stylesheet (patch; KdeUiProxyStyle problem)
From:       David Faure <faure () kde ! org>
Date:       2008-01-28 14:48:24
Message-ID: 200801281548.24686.faure () kde ! org
[Download RAW message or body]

During the KDE4 release-party/conference in Toulouse, Aurélien Gateau was demo'ing
the use of CSS stylesheets in Qt, and someone asked whether it was possible for a
user to set a global stylesheet to be used by all KDE applications.
This seems like a very good idea indeed, for easy-to-write widget "themes", 
and for accessibility purposes as well.

During the next talk and afterwards I hacked it up and it works sort of fine...
(good enough for a demo a few hours later, to make the audience go "wow" ;)

The patch adds the GUI (KUrlRequester) for a user-specified stylesheet in `kcmshell4 \
style`,  the KGlobalSettings code for using it, including honouring the --stylesheet \
argument so that this one has priority over the kde-global stylesheet [you can ignore \
the kcmdlineargs.cpp patch for trunk, which already has that line]. The apply button \
propagates the setting to all running kde applications, much like when changing \
widget styles.

There are 3 problems with this feature though.

The main problem is that any code using a proxy style like KdeUiProxyStyle \
(KLineEdit) and KonqProxyStyle (konqueror) crashes due to infinite recursion, the \
proxy style calls the real style, which is in fact the stylesheet-style, which calls \
the "base" style which is the kdeuiproxystyle, etc. I don't have a solution for this; \
this mail is about getting the other patches out in the open in case someone's \
interested, and for when the proxy-style issue is fixed :)

Another problem is that stylesheets usually refer to images, which are found in the \
"current working directory", but that solution doesn't work for applications with \
random working directories. It seems to me that we need a setStyleSheetDirectory() in \
QApplication and QWidget? Or what do I miss?

This feature triggers another bug: konsole switches to a proportional font when \
pressing the apply button in kcmshell4 style; this is again because of Qt's wrong \
logic of "when you use a stylesheet, the widget font is completely ignored", which \
still makes no sense to me even though it's documented that way. This is the same \
problem as the one which made use switch to a proxy style for klineedit iirc, is \
supposed to be fixed by Qt-4.4?

-- 
David Faure, faure@kde.org, sponsored by Trolltech to work on KDE,
Konqueror (http://www.konqueror.org), and KOffice (http://www.koffice.org).


["kdebase_kcmstyle.diff" (text/x-diff)]

Index: kcmstyle.h
===================================================================
--- kcmstyle.h	(revision 767259)
+++ kcmstyle.h	(working copy)
@@ -37,6 +37,7 @@
 
 #include "menupreview.h"
 
+class KUrlRequester;
 class KComboBox;
 class KConfig;
 class QCheckBox;
@@ -88,6 +89,7 @@ protected Q_SLOTS:
 	void setStyleDirty();
 
 	void styleChanged();
+	void styleSheetChanged();
 	void menuEffectChanged( bool enabled );
 	void menuEffectChanged();
 	void menuEffectTypeChanged();
@@ -149,6 +151,7 @@ private:
 	QCheckBox* cbHoverButtons;
 	QCheckBox* cbTransparentToolbars;
 	QCheckBox* cbEnableTooltips;
+    KUrlRequester* styleSheetRequester;
 	QComboBox* comboToolbarIcons;
 
 	QCheckBox* cbIconsOnButtons;
Index: kcmstyle.cpp
===================================================================
--- kcmstyle.cpp	(revision 767259)
+++ kcmstyle.cpp	(working copy)
@@ -22,6 +22,8 @@
  */
 
 #include "kcmstyle.h"
+#include <kdebug.h>
+#include <kurlrequester.h>
 
 #include <config-X11.h>
 
@@ -216,6 +218,15 @@ KCMStyle::KCMStyle( QWidget* parent, con
 	gbWidgetStyleLayout->addWidget( cbTearOffHandles );
 	cbTearOffHandles->hide(); // reenable when the corresponding Qt method is virtual \
and properly reimplemented  
+        QHBoxLayout* styleSheetHBox = new QHBoxLayout();
+        gbWidgetStyleLayout->addLayout(styleSheetHBox);
+        QLabel* styleSheetLabel = new QLabel(i18n("Stylesheet"), gbWidgetStyle);
+        styleSheetHBox->addWidget(styleSheetLabel);
+        styleSheetRequester = new KUrlRequester( gbWidgetStyle );
+        styleSheetRequester->setMode(KFile::File | KFile::ExistingOnly | \
KFile::LocalOnly); +        styleSheetHBox->addWidget(styleSheetRequester);
+	connect( styleSheetRequester,  SIGNAL(textChanged(QString)), this, \
SLOT(styleSheetChanged())); +
 	QGroupBox *gbPreview = new QGroupBox( i18n( "Preview" ), page1 );
 	QVBoxLayout *previewLayout = new QVBoxLayout(gbPreview);
 	previewLayout->setMargin( 0 );
@@ -400,6 +411,7 @@ KCMStyle::KCMStyle( QWidget* parent, con
 	connect( cbHoverButtons,       SIGNAL(toggled(bool)),   this, \
SLOT(setToolbarsDirty()));  connect( cbTransparentToolbars, SIGNAL(toggled(bool)),   \
this, SLOT(setToolbarsDirty()));  connect( cbEnableTooltips,     \
SIGNAL(toggled(bool)),   this, SLOT(setEffectsDirty())); +	connect( \
styleSheetRequester,  SIGNAL(textChanged(QString)), this, SLOT(setStyleDirty()));  \
connect( cbIconsOnButtons,     SIGNAL(toggled(bool)),   this, \
SLOT(setEffectsDirty()));  connect( cbTearOffHandles,     SIGNAL(toggled(bool)),   \
this, SLOT(setEffectsDirty()));  connect( comboToolbarIcons,    \
SIGNAL(activated(int)), this, SLOT(setToolbarsDirty())); @@ -618,6 +630,7 @@ void \
KCMStyle::save()  
     KConfigGroup generalGroup(&_config, "General");
     generalGroup.writeEntry("widgetStyle", currentStyle());
+    generalGroup.writeEntry("styleSheet", styleSheetRequester->url().path());
 
     KConfigGroup toolbarStyleGroup(&_config, "Toolbar style");
     toolbarStyleGroup.writeEntry("Highlighting", cbHoverButtons->isChecked(), \
KConfig::Normal|KConfig::Global); @@ -732,6 +745,7 @@ void KCMStyle::defaults()
 	cbHoverButtons->setChecked(true);
 	cbTransparentToolbars->setChecked(true);
 	cbEnableTooltips->setChecked(true);
+        styleSheetRequester->clear();
 	comboToolbarIcons->setCurrentIndex(0);
 	cbIconsOnButtons->setChecked(true);
 	cbTearOffHandles->setChecked(false);
@@ -861,6 +875,8 @@ void KCMStyle::loadStyle( KConfig& confi
 	m_bStyleDirty = false;
 
 	switchStyle( currentStyle() );	// make resets visible
+
+        styleSheetRequester->setPath(configGroup.readEntry("styleSheet"));
 }
 
 QString KCMStyle::currentStyle()
@@ -874,6 +890,26 @@ void KCMStyle::styleChanged()
 	switchStyle( currentStyle() );
 }
 
+void KCMStyle::styleSheetChanged()
+{
+    QString styleSheet = styleSheetRequester->url().path();
+    kDebug() << styleSheet;
+    QByteArray styleSheetData;
+    if (!styleSheet.isEmpty()) {
+        QFile file(styleSheet);
+        if (file.open(QIODevice::ReadOnly)) {
+            styleSheetData = file.readAll();
+        }
+    }
+    if (!styleSheetData.isEmpty()) {
+        // TODO There should be a setStyleSheetDirectory....
+        stylePreview->setStyleSheet(QString::fromUtf8(styleSheetData));
+    } else {
+        stylePreview->setStyleSheet(QString());
+        // Also reset application stylesheet otherwise (if there was one), we can \
see no changes +        qApp->setStyleSheet(QString());
+    }
+}
 
 void KCMStyle::switchStyle(const QString& styleName, bool force)
 {
@@ -1004,11 +1040,11 @@ void KCMStyle::loadEffects( KConfig& con
 	}
 
 	m_bEffectsDirty = false;
-}
+    }
 
 
-void KCMStyle::menuEffectTypeChanged()
-{
+    void KCMStyle::menuEffectTypeChanged()
+    {
 	MenuPreview::PreviewMode mode;
 
 	if (comboMenuEffect->currentIndex() != 3)
@@ -1021,33 +1057,33 @@ void KCMStyle::menuEffectTypeChanged()
 	menuPreview->setPreviewMode(mode);
 
 	m_bEffectsDirty = true;
-}
+    }
 
 
-void KCMStyle::menuEffectChanged()
-{
+    void KCMStyle::menuEffectChanged()
+    {
 	menuEffectChanged( cbEnableEffects->isChecked() );
 	m_bEffectsDirty = true;
-}
+    }
 
 
-void KCMStyle::menuEffectChanged( bool enabled )
-{
+    void KCMStyle::menuEffectChanged( bool enabled )
+    {
 	if (enabled &&
 		comboMenuEffect->currentIndex() == 3) {
 		menuContainer->setEnabled(true);
 	} else
 		menuContainer->setEnabled(false);
 	m_bEffectsDirty = true;
-}
+    }
 
 
 // ----------------------------------------------------------------
 // All the Miscellaneous stuff
 // ----------------------------------------------------------------
 
-void KCMStyle::loadMisc( KConfig& config )
-{
+    void KCMStyle::loadMisc( KConfig& config )
+    {
 	// KDE's Part via KConfig
 	KConfigGroup configGroup = config.group("Toolbar style");
 	cbHoverButtons->setChecked(configGroup.readEntry("Highlighting", true));
@@ -1069,10 +1105,10 @@ void KCMStyle::loadMisc( KConfig& config
 	cbTearOffHandles->setChecked(configGroup.readEntry("InsertTearOffHandle", false));
 
 	m_bToolbarsDirty = false;
-}
+    }
 
-void KCMStyle::addWhatsThis()
-{
+    void KCMStyle::addWhatsThis()
+    {
 	// Page1
 	cbStyle->setWhatsThis( i18n("Here you can choose from a list of"
 							" predefined widget styles (e.g. the way buttons are drawn) which"
@@ -1127,8 +1163,9 @@ void KCMStyle::addWhatsThis()
 							"show so called tear-off handles. If you click them, you get the menu "
 							"inside a widget. This can be very helpful when performing "
 							"the same action multiple times.") );
-}
+    }
 
 #include "kcmstyle.moc"
 
 // vim: set noet ts=4:
+
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt	(revision 767259)
+++ CMakeLists.txt	(working copy)
@@ -12,7 +12,7 @@ kde4_add_ui_files(kcm_style_PART_SRCS st
 
 kde4_add_plugin(kcm_style ${kcm_style_PART_SRCS})
 
-target_link_libraries(kcm_style ${KDE4_KDEUI_LIBS} ${BLITZ_LIBRARIES})
+target_link_libraries(kcm_style ${KDE4_KIO_LIBS} ${BLITZ_LIBRARIES})
 
 install(TARGETS kcm_style  DESTINATION ${PLUGIN_INSTALL_DIR})
 


["kdelibs_global_stylesheet.diff" (text/x-diff)]

Index: kdecore/kernel/kcmdlineargs.cpp
===================================================================
--- kdecore/kernel/kcmdlineargs.cpp	(revision 767562)
+++ kdecore/kernel/kcmdlineargs.cpp	(working copy)
@@ -298,6 +298,7 @@ KCmdLineArgsStatic::KCmdLineArgsStatic (
     qt_options.add("qws", ki18n("forces the application to run as QWS Server"));
 #endif
     qt_options.add("reverse", ki18n("mirrors the whole layout of widgets"));
+    qt_options.add("stylesheet <file>",   ki18n("sets the application stylesheet"));
 
     // KDE options
     kde_options.add("caption <caption>",   ki18n("Use 'caption' as name in the titlebar"));
Index: kdeui/kernel/kapplication.cpp
===================================================================
--- kdeui/kernel/kapplication.cpp	(revision 767562)
+++ kdeui/kernel/kapplication.cpp	(working copy)
@@ -832,6 +832,12 @@ void KApplicationPrivate::parseCommandLi
     }
 #endif
 
+    KCmdLineArgs *qtArgs = KCmdLineArgs::parsedArgs("qt");
+    if (qtArgs && qtArgs->isSet("stylesheet")) {
+        extern QString kde_overrideStyleSheet; // see KGlobalSettings. Should we have a static setter?
+        kde_overrideStyleSheet = qtArgs->getOption("stylesheet");
+    }
+
     if ( q->type() != KApplication::Tty ) {
         if (args && args->isSet("icon"))
         {
Index: kdeui/kernel/kglobalsettings.cpp
===================================================================
--- kdeui/kernel/kglobalsettings.cpp	(revision 767562)
+++ kdeui/kernel/kglobalsettings.cpp	(working copy)
@@ -822,9 +822,9 @@ void KGlobalSettings::Private::_k_slotNo
     }
 }
 
-// Set by KApplication - which is now in kdeui so this needs to be exported
-// In the long run, KGlobalSettings probably belongs to kdeui as well...
+// Set by KApplication
 QString kde_overrideStyle;
+QString kde_overrideStyleSheet;
 
 void KGlobalSettings::Private::applyGUIStyle()
 {
@@ -848,11 +848,24 @@ void KGlobalSettings::Private::applyGUIS
         if ( !sp && styleStr != defaultStyle)
             sp = QStyleFactory::create( defaultStyle );
         if ( !sp )
-            sp = QStyleFactory::create( *(QStyleFactory::keys().begin()) );
+            sp = QStyleFactory::create(QStyleFactory::keys().first());
         qApp->setStyle(sp);
     }
     else
         qApp->setStyle(kde_overrideStyle);
+
+    QString styleSheet = pConfig.readEntry("styleSheet", QString());
+    if (!styleSheet.isEmpty() && kde_overrideStyleSheet.isEmpty()) {
+        QFile file(styleSheet);
+        if (file.open(QIODevice::ReadOnly)) {
+            QByteArray data = file.readAll();
+            if (!data.isEmpty()) {
+                // TODO There should be a setStyleSheetDirectory....
+                qApp->setStyleSheet(QString::fromUtf8(data));
+            }
+        }
+    }
+
     // Reread palette from config file.
     kdisplaySetPalette();
 }
Index: kdeui/widgets/klineedit.cpp
===================================================================
--- kdeui/widgets/klineedit.cpp	(revision 767562)
+++ kdeui/widgets/klineedit.cpp	(working copy)
@@ -192,9 +192,10 @@ void KLineEdit::init()
     if ( !d->previousHighlightColor.isValid() )
       d->previousHighlightColor=p.color(QPalette::Normal,QPalette::Highlight);
     
-    QStyle *lineEditStyle = new KLineEditStyle(this);
-    lineEditStyle->setParent(this);
-    setStyle(lineEditStyle);
+    // TODO fix recursion
+    //QStyle *lineEditStyle = new KLineEditStyle(this);
+    //lineEditStyle->setParent(this);
+    //setStyle(lineEditStyle);
 }
 
 QString KLineEdit::clickMessage() const


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic