Commit b52c0ceb authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇

[Colors KCM] Port to new design

Overall the user experience has been streamlined and simplified a lot:

* The "Default" theme option has been dropped in favor of having the "Defaults" button revert the selected theme to Breeze.
  While technically the old code made it read the hardcoded default colors in KColorScheme (which cause the window decoration
  to turn blue as it cannot write into KWin config like the theme files can), this change makes most sense from a UX POV.
* The "Current" theme option has also been removed. Technically, when applying a theme the colors are copied into kdeglobals,
  so you could have a custom theme that is not an actual .colors file on disk. However, this is imho quite a niche usecase.
  Ideally, we showed a "Custom" theme as soon as the actual theme diverges from any theme file installed but that would require
  tediously comparing dozens of settings values which I don't think is feasible. At least when the color scheme name set in
  kdeglobals does not exist, a warning is now displayed.
* The "Apply to non-Qt applications checkbox" which isn't something one would want to uncheck has been removed.
  It is still read from kcmdisplayrc for those who really want to disable it but there is no user-visible checkbox anymore.

KColorSchemeEditor is now completely disentangled from the KCM and is merely launched as separate process:

* When editing a system scheme, upon clicking "Save" the user is prompted to type a new scheme name. This ensures that any custom
  scheme is always present on disk reducing the need for a "Current" entry. When the dialog is then closed, the newly saved theme
  is selected.
* When editing a user scheme, the "Save" button turns into "Apply", which when clicked updates the scheme with any changes made in
  the dialog (different behavior from when kcolorschemeeditor is launched standalone, where "Save" is always essentially "Save As")

The rewrite also comes with all the goodies we got in the other new KCMs, such as the ability to drop a .colors file into the view
to install it (even from remote locations), undo deletion until you apply your changes, double click for quick apply, and so on.

BUG: 307216
FIXED-IN: 5.16.0

Differential Revision: https://phabricator.kde.org/D12278
parent fdeb8bb3
# KI18N Translation Domain for this library
add_definitions(-DTRANSLATION_DOMAIN=\"kcmcolors\")
set(scheme_editor_SRCS
kcolorschemeeditor.cpp
scmeditordialog.cpp
scmeditoroptions.cpp
scmeditorcolors.cpp
scmeditoreffects.cpp
previewwidget.cpp
setpreviewwidget.cpp
colorscm.cpp
../krdb/krdb.cpp
)
set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml)
ki18n_wrap_ui(scheme_editor_SRCS
colorsettings.ui
scmeditordialog.ui
scmeditoroptions.ui
scmeditorcolors.ui
scmeditoreffects.ui
preview.ui
setpreview.ui
)
qt5_add_dbus_interface(scheme_editor_SRCS ${klauncher_xml} klauncher_iface)
add_executable(kcolorschemeeditor ${scheme_editor_SRCS})
target_link_libraries(kcolorschemeeditor
KF5::KCMUtils
KF5::GuiAddons
KF5::I18n
KF5::KIOCore
KF5::CoreAddons
KF5::NewStuff
KF5::WindowSystem
)
if(X11_FOUND)
target_link_libraries(kcolorschemeeditor ${X11_LIBRARIES} Qt5::X11Extras)
endif()
install(TARGETS kcolorschemeeditor DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES org.kde.kcolorschemeeditor.desktop DESTINATION ${KDE_INSTALL_APPDIR})
# ----------------
add_definitions(-DTRANSLATION_DOMAIN=\"kcm_colors\")
set(kcm_colors_SRCS
../krdb/krdb.cpp
colorscm.cpp
scmeditordialog.cpp
scmeditoroptions.cpp
scmeditorcolors.cpp
scmeditoreffects.cpp
previewwidget.cpp
setpreviewwidget.cpp
)
colors.cpp
)
qt5_add_dbus_interface(kcm_colors_SRCS ${klauncher_xml} klauncher_iface)
ki18n_wrap_ui(kcm_colors_SRCS
colorsettings.ui
scmeditordialog.ui
scmeditoroptions.ui
scmeditorcolors.ui
scmeditoreffects.ui
preview.ui
setpreview.ui)
# needed for krdb
qt5_add_dbus_interface(kcm_colors_SRCS ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml klauncher_iface)
add_library(kcm_colors MODULE ${kcm_colors_SRCS})
add_dependencies(kcm_colors kcolorschemeeditor)
target_link_libraries(kcm_colors
Qt5::DBus
KF5::KCMUtils
KF5::CoreAddons
KF5::Declarative
KF5::GuiAddons
KF5::I18n
KF5::KIOCore
KF5::CoreAddons
Qt5::DBus
KF5::KIOWidgets
KF5::NewStuff
KF5::WindowSystem)
KF5::QuickAddons
KF5::WindowSystem
)
if(X11_FOUND)
target_link_libraries(kcm_colors ${X11_LIBRARIES} Qt5::X11Extras)
endif()
install(TARGETS kcm_colors DESTINATION ${KDE_INSTALL_PLUGINDIR})
install( FILES colors.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} )
install( FILES colorschemes.knsrc DESTINATION ${KDE_INSTALL_CONFDIR} )
kcoreaddons_desktop_to_json(kcm_colors "kcm_colors.desktop")
install(FILES kcm_colors.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
install(TARGETS kcm_colors DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms)
install(FILES colorschemes.knsrc DESTINATION ${KDE_INSTALL_CONFDIR})
kpackage_install_package(package kcm_colors kcms)
# built-in color schemes
file(GLOB schemefiles schemes/*.colors)
install( FILES ${schemefiles} DESTINATION ${KDE_INSTALL_DATADIR}/color-schemes )
add_subdirectory(editor)
#! /usr/bin/env bash
$EXTRACTRC *.ui >> rc.cpp
$XGETTEXT *.cpp -o $podir/kcmcolors.pot
$XGETTEXT `find . -name "*.cpp" -o -name "*.qml"` -o $podir/kcm_colors.pot
rm -f rc.cpp
query kwin for what WM colors should be shown for configuring(?)
options (i.e. "things on the first tab") not saved in schemes (in kdeglobals only)
sort schemes list, 'current' at 0, 'default' at 1, everything else sorted (possibly kde3-style, user schemes on top?)
This diff is collapsed.
/*
* Copyright (c) 2019 Kai Uwe Broulik <kde@privat.broulik.de>
*
* 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) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* 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/>.
*/
#pragma once
#include <QScopedPointer>
#include <QPointer>
#include <KSharedConfig>
#include <KNewStuff3/KNS3/DownloadDialog>
#include <KQuickAddons/ConfigModule>
class QStandardItemModel;
class QProcess;
class QTemporaryFile;
namespace KIO
{
class FileCopyJob;
}
class KCMColors : public KQuickAddons::ConfigModule
{
Q_OBJECT
Q_PROPERTY(QStandardItemModel *colorsModel READ colorsModel CONSTANT)
Q_PROPERTY(QString selectedScheme READ selectedScheme WRITE setSelectedScheme NOTIFY selectedSchemeChanged)
Q_PROPERTY(int selectedSchemeIndex READ selectedSchemeIndex NOTIFY selectedSchemeIndexChanged)
Q_PROPERTY(bool downloadingFile READ downloadingFile NOTIFY downloadingFileChanged)
public:
KCMColors(QObject *parent, const QVariantList &args);
~KCMColors() override;
enum Roles {
SchemeNameRole = Qt::UserRole + 1,
PaletteRole,
RemovableRole,
PendingDeletionRole
};
QStandardItemModel *colorsModel() const;
QString selectedScheme() const;
void setSelectedScheme(const QString &scheme);
int selectedSchemeIndex() const;
bool downloadingFile() const;
Q_INVOKABLE void getNewStuff(QQuickItem *ctx);
Q_INVOKABLE void installSchemeFromFile(const QUrl &url);
Q_INVOKABLE void setPendingDeletion(int index, bool pending);
Q_INVOKABLE void editScheme(int index, QQuickItem *ctx);
public Q_SLOTS:
void load() override;
void save() override;
void defaults() override;
Q_SIGNALS:
void selectedSchemeChanged();
void selectedSchemeIndexChanged();
void downloadingFileChanged();
void showSuccessMessage(const QString &message);
void showErrorMessage(const QString &message);
void showSchemeNotInstalledWarning(const QString &schemeName);
private:
void loadModel();
void saveColors();
void processPendingDeletions();
int indexOfScheme(const QString &schemeName) const;
void installSchemeFile(const QString &path);
QStandardItemModel *m_model;
QString m_selectedScheme;
bool m_selectedSchemeDirty = false;
bool m_applyToAlien = true;
QPointer<KNS3::DownloadDialog> m_newStuffDialog;
QProcess *m_editDialogProcess = nullptr;
KSharedConfigPtr m_config;
QScopedPointer<QTemporaryFile> m_tempInstallFile;
QPointer<KIO::FileCopyJob> m_tempCopyJob;
};
This diff is collapsed.
/* KDE Display color scheme setup module
* Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
* Copyright (C) 2007 Jeremy Whiting <jpwhiting@kde.org>
* Copyright (C) 2016 Olivier Churlaud <olivier@churlaud.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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __COLORSCM_H__
#define __COLORSCM_H__
#include <KCModule>
#include "ui_colorsettings.h"
class QStackedWidget;
class QListWidgetItem;
class KColorButton;
/**
* The Desktop/Colors tab in kcontrol.
*/
class KColorCm : public KCModule, public Ui::colorSettings
{
Q_OBJECT
public:
KColorCm(QWidget *parent, const QVariantList &);
~KColorCm() override;
public Q_SLOTS:
/**
* load the settings from the config
*/
void load() override;
/**
* save the current settings
*/
void save() override;
/**
* sets the configuration to sensible default values.
*/
void defaults() override;
/**
* Update all keys of the Global config with the theme ones.
*/
void updateConfig(KSharedConfigPtr config);
private Q_SLOTS:
/**
* slot called when the schemeList selection changes.
*
* It loads the scheme with loadScheme(KSharedConfigPtr), which updates the
* config (updateConfig())
*/
void loadScheme(QListWidgetItem *currentItem, QListWidgetItem *previousItem);
/**
* Reselect the previously selected scheme in schemeList without loading it
*/
void selectPreviousSchemeAgain();
/**
* Slot called when the remove button is clicked
*/
void on_schemeRemoveButton_clicked();
/**
* Slot called when the import button is clicked
*/
void on_schemeImportButton_clicked();
/**
* slot called when the get new schemes button is clicked
*/
void on_schemeKnsButton_clicked();
/**
* Slot called when the Edit button is clicked.
*
* It opens a dialog for the edition/creation.
*/
void on_schemeEditButton_clicked();
private:
/**
* Create a preview icon of a color scheme
*/
static QPixmap createSchemePreviewIcon(const KSharedConfigPtr &config);
/**
* Load from global.
*/
void loadInternal();
/**
* load a scheme from a config file at a given path and updates the
* config (updateConfig())
*/
void loadScheme(KSharedConfigPtr config);
/**
* Populate the schemeList with color schemes found on the system
*/
void populateSchemeList();
QString m_currentColorScheme;
// The global config
KSharedConfigPtr m_config;
// don't (re)load the scheme, only select it in schemeList
bool m_dontLoadSelectedScheme;
// the item previously selected in schemeList
QListWidgetItem *m_previousSchemeItem;
};
#endif
set(scheme_editor_SRCS
kcolorschemeeditor.cpp
scmeditordialog.cpp
scmeditoroptions.cpp
scmeditorcolors.cpp
scmeditoreffects.cpp
previewwidget.cpp
setpreviewwidget.cpp
)
ki18n_wrap_ui(scheme_editor_SRCS
colorsettings.ui
scmeditordialog.ui
scmeditoroptions.ui
scmeditorcolors.ui
scmeditoreffects.ui
preview.ui
setpreview.ui
)
add_executable(kcolorschemeeditor ${scheme_editor_SRCS})
target_link_libraries(kcolorschemeeditor
KF5::ConfigWidgets
KF5::GuiAddons
KF5::I18n
KF5::CoreAddons
KF5::NewStuff
KF5::WindowSystem
)
install(TARGETS kcolorschemeeditor DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES org.kde.kcolorschemeeditor.desktop DESTINATION ${KDE_INSTALL_APPDIR})
......@@ -24,10 +24,10 @@
#include <QTextStream>
#include <KAboutData>
#include <KWindowSystem>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
KAboutData aboutData(
......@@ -48,6 +48,13 @@ int main(int argc, char* argv[])
QCommandLineParser parser;
parser.addPositionalArgument("theme", i18n("Scheme to edit or to use as a base."),
QStringLiteral("kcolorschemeeditor ThemeName"));
QCommandLineOption overwriteOption(QStringLiteral("overwrite"), i18n("Overwrite edited theme when saving"));
parser.addOption(overwriteOption);
QCommandLineOption attachOption(QStringLiteral("attach"), i18n("Makes the dialog transient for another application window specified by handle"), QStringLiteral("handle"));
parser.addOption(attachOption);
aboutData.setupCommandLine(&parser);
parser.process(app);
aboutData.processCommandLine(&parser);
......@@ -67,6 +74,23 @@ int main(int argc, char* argv[])
}
SchemeEditorDialog dialog(path);
dialog.setOverwriteOnSave(parser.isSet(overwriteOption));
// FIXME doesn't work :(
const QString attachHandle = parser.value(attachOption);
if (!attachHandle.isEmpty()) {
// TODO wayland: once we have foreign surface support
const QString x11Prefix = QStringLiteral("x11:");
if (attachHandle.startsWith(x11Prefix)) {
bool ok = false;
WId winId = attachHandle.mid(x11Prefix.length()).toLong(&ok, 0);
if (ok) {
dialog.setModal(true);
KWindowSystem::setMainWindow(&dialog, winId);
}
}
}
dialog.show();
......
......@@ -21,7 +21,6 @@
#include "scmeditoroptions.h"
#include "scmeditorcolors.h"
#include "scmeditoreffects.h"
#include "colorscm.h"
#include <QDebug>
#include <QDir>
......@@ -32,25 +31,20 @@
#include <KConfigGroup>
#include <KColorScheme>
#include <KMessageBox>
#include <KWindowSystem>
#include <KNS3/UploadDialog>
SchemeEditorDialog::SchemeEditorDialog(KSharedConfigPtr config, KColorCm *parent)
: QDialog( parent )
, m_disableUpdates(false)
, m_unsavedChanges(false)
, m_kcm(parent)
SchemeEditorDialog::SchemeEditorDialog(KSharedConfigPtr config, QWidget *parent)
: QDialog(parent)
{
m_config = config;
init();
}
SchemeEditorDialog::SchemeEditorDialog(const QString &path, KColorCm *parent)
: QDialog( parent )
SchemeEditorDialog::SchemeEditorDialog(const QString &path, QWidget *parent)
: QDialog(parent)
, m_filePath(path)
, m_disableUpdates(false)
, m_unsavedChanges(false)
, m_kcm(parent)
{
m_config = KSharedConfig::openConfig(path);
......@@ -59,15 +53,23 @@ SchemeEditorDialog::SchemeEditorDialog(const QString &path, KColorCm *parent)
init();
}
bool SchemeEditorDialog::overwriteOnSave() const
{
return m_overwriteOnSave;
}
void SchemeEditorDialog::setOverwriteOnSave(bool overwrite)
{
m_overwriteOnSave = overwrite;
buttonBox->button(QDialogButtonBox::Apply)->setVisible(overwrite);
buttonBox->button(QDialogButtonBox::Save)->setVisible(!overwrite);
}
void SchemeEditorDialog::init()
{
setupUi(this);
//we are in standalone appication mode? don't show the apply button
if (!m_kcm) {
buttonBox->button(QDialogButtonBox::Apply)->setVisible(false);
}
schemeKnsUploadButton->setIcon( QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")) );
m_optionTab = new SchemeEditorOptions(m_config);
......@@ -84,6 +86,10 @@ void SchemeEditorDialog::init()
connect(m_disabledTab, &SchemeEditorEffects::changed, this, &SchemeEditorDialog::updateTabs);
connect(m_inactiveTab, &SchemeEditorEffects::changed, this, &SchemeEditorDialog::updateTabs);
// In overwrite mode we use "Apply", in regular mode "Save" button
buttonBox->button(QDialogButtonBox::Apply)->setVisible(false);
buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
buttonBox->button(QDialogButtonBox::Reset)->setEnabled(false);
updateTabs();
......@@ -125,15 +131,11 @@ void SchemeEditorDialog::on_buttonBox_clicked(QAbstractButton *button)
updateTabs();
setUnsavedChanges(false);
}
else if (buttonBox->standardButton(button) == QDialogButtonBox::Save)
else if (buttonBox->standardButton(button) == QDialogButtonBox::Save
|| buttonBox->standardButton(button) == QDialogButtonBox::Apply)
{
saveScheme();
}
else if (buttonBox->standardButton(button) == QDialogButtonBox::Apply)
{
applyScheme();
emit applied();
}
else if (buttonBox->standardButton(button) == QDialogButtonBox::Close)
{
if (m_unsavedChanges) {
......@@ -151,25 +153,18 @@ void SchemeEditorDialog::on_buttonBox_clicked(QAbstractButton *button)
}
}
void SchemeEditorDialog::applyScheme()
{
if (!m_kcm) {
return;
}
m_kcm->updateConfig(m_config);
m_kcm->save();
}
void SchemeEditorDialog::saveScheme()
{
QString name = m_schemeName;
// prompt for the name to save as
bool ok;
QString schemeName = KConfigGroup(m_config, "General").readEntry("Name");
QString name = QInputDialog::getText(this, i18n("Save Color Scheme"),
i18n("&Enter a name for the color scheme:"), QLineEdit::Normal, m_schemeName, &ok);
if (!ok) {
return;
if (!m_overwriteOnSave) {
bool ok;
name = QInputDialog::getText(this, i18n("Save Color Scheme"),
i18n("&Enter a name for the color scheme:"), QLineEdit::Normal, m_schemeName, &ok);
if (!ok) {
return;
}
}
QString filename = name;
......@@ -190,7 +185,7 @@ void SchemeEditorDialog::saveScheme()
// or if we can overwrite it if it exists
if (path.isEmpty() || !file.exists() || canWrite)
{
if(canWrite){
if(canWrite && !m_overwriteOnSave){
int ret = KMessageBox::questionYesNo(this,
i18n("A color scheme with that name already exists.\nDo you want to overwrite it?"),
i18n("Save Color Scheme"),
......@@ -226,6 +221,9 @@ void SchemeEditorDialog::saveScheme()
setWindowTitle(name);
setUnsavedChanges(false);
QTextStream out(stdout);
out << filename << endl;
}
else if (!canWrite && file.exists())
{
......@@ -262,11 +260,13 @@ void SchemeEditorDialog::setUnsavedChanges(bool changes)
if (changes)
{
buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
buttonBox->button(QDialogButtonBox::Reset)->setEnabled(true);
}
else
{
buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
buttonBox->button(QDialogButtonBox::Reset)->setEnabled(false);
}
}
<