Changeset View
Standalone View
kcmkwin/kwindecoration/kcm.cpp
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * Copyright 2014 Martin Gräßlin <mgraesslin@kde.org> | 2 | * Copyright (c) 2019 Valerio Pilo <vpilo@coldshock.net> | ||
3 | * | 3 | * | ||
4 | * This program is free software; you can redistribute it and/or | 4 | * This library is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License as | 5 | * modify it under the terms of the GNU Library General Public | ||
6 | * published by the Free Software Foundation; either version 2 of | 6 | * License version 2 as published by the Free Software Foundation. | ||
7 | * the License or (at your option) version 3 or any later version | | |||
8 | * accepted by the membership of KDE e.V. (or its successor approved | | |||
9 | * by the membership of KDE e.V.), which shall act as a proxy | | |||
10 | * defined in Section 14 of version 3 of the license. | | |||
11 | * | 7 | * | ||
12 | * This program is distributed in the hope that it will be useful, | 8 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * GNU General Public License for more details. | 11 | * Library General Public License for more details. | ||
16 | * | 12 | * | ||
17 | * You should have received a copy of the GNU General Public License | 13 | * You should have received a copy of the GNU Library General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 14 | * along with this library; see the file COPYING.LIB. If not, write to | ||
15 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||||
16 | * Boston, MA 02110-1301, USA. | ||||
19 | */ | 17 | */ | ||
18 | | ||||
20 | #include "kcm.h" | 19 | #include "kcm.h" | ||
21 | #include "decorationmodel.h" | 20 | #include "decorationmodel.h" | ||
22 | #include "declarative-plugin/buttonsmodel.h" | 21 | #include "declarative-plugin/buttonsmodel.h" | ||
23 | #include <config-kwin.h> | 22 | #include <config-kwin.h> | ||
24 | 23 | | |||
25 | // KDE | 24 | #include <KAboutData> | ||
26 | #include <KConfigGroup> | 25 | #include <KConfigGroup> | ||
26 | #include <KLocalizedString> | ||||
27 | #include <KPluginFactory> | 27 | #include <KPluginFactory> | ||
28 | #include <KSharedConfig> | 28 | #include <KNSCore/Engine> | ||
29 | #include <KDecoration2/DecorationButton> | 29 | | ||
30 | #include <KNewStuff3/KNS3/DownloadDialog> | | |||
31 | #include <kdeclarative/kdeclarative.h> | | |||
32 | // Qt | | |||
33 | #include <QDBusConnection> | 30 | #include <QDBusConnection> | ||
34 | #include <QDBusMessage> | 31 | #include <QDBusMessage> | ||
35 | #include <QFontDatabase> | 32 | #include <QDebug> | ||
36 | #include <QMenu> | | |||
37 | #include <QQmlContext> | | |||
38 | #include <QQmlEngine> | | |||
39 | #include <QQuickItem> | 33 | #include <QQuickItem> | ||
40 | #include <QQuickView> | 34 | #include <QQuickWindow> | ||
41 | #include <QSortFilterProxyModel> | 35 | #include <QSortFilterProxyModel> | ||
42 | #include <QStandardPaths> | | |||
43 | #include <QVBoxLayout> | | |||
44 | 36 | | |||
45 | K_PLUGIN_FACTORY(KDecorationFactory, | 37 | #include <KNewStuff3/KNS3/DownloadDialog> | ||
46 | registerPlugin<KDecoration2::Configuration::ConfigurationModule>(); | 38 | | ||
47 | ) | 39 | | ||
40 | K_PLUGIN_FACTORY_WITH_JSON(KCMKWinDecorationFactory, "kwindecoration.json", registerPlugin<KCMKWinDecoration>();) | ||||
48 | 41 | | |||
49 | Q_DECLARE_METATYPE(KDecoration2::BorderSize) | 42 | Q_DECLARE_METATYPE(KDecoration2::BorderSize) | ||
50 | 43 | | |||
51 | namespace KDecoration2 | 44 | /* | ||
52 | { | 45 | FIXME In main.qml, In other modules and in the help the main item is just a child, not set as contentItem. But if placed as a child, | ||
davidedmundson: See next comment | |||||
46 | the UI can be scrolled with press&drag and behaves erratically. What's wrong there? | ||||
53 | 47 | | |||
54 | namespace Configuration | | |||
55 | { | | |||
56 | static const QString s_pluginName = QStringLiteral("org.kde.kdecoration2"); | | |||
57 | #if HAVE_BREEZE_DECO | | |||
58 | static const QString s_defaultPlugin = QStringLiteral(BREEZE_KDECORATION_PLUGIN_ID); | | |||
59 | static const QString s_defaultTheme; | | |||
60 | #else | | |||
61 | static const QString s_defaultPlugin = QStringLiteral("org.kde.kwin.aurorae"); | | |||
62 | static const QString s_defaultTheme = QStringLiteral("kwin4_decoration_qml_plastik"); | | |||
63 | #endif | | |||
64 | static const QString s_borderSizeNormal = QStringLiteral("Normal"); | | |||
65 | static const QString s_ghnsIcon = QStringLiteral("get-hot-new-stuff"); | | |||
66 | 48 | | |||
67 | ConfigurationForm::ConfigurationForm(QWidget *parent) | 49 | FIXME I get these warnings starting the module. Am I doing something wrong here? | ||
68 | : QWidget(parent) | 50 | /usr/lib/x86_64-linux-gnu/qml/org/kde/kcm/SimpleKCM.qml:107: TypeError: Cannot read property 'atYBeginning' of null | ||
SimpleKCM expects there to be a central flickable Item See the base class Kirigami.ScrollablePage Given our main central item is the tabview we might be best off inheriting from Kirigami.Page davidedmundson: SimpleKCM expects there to be a central flickable Item
See the base class Kirigami. | |||||
Setting a couple properties for the Page's flickable item seems to fix the issue, but I fear it's not enough: look at the last image of this gallery vpilo: Setting a couple properties for the Page's `flickable` item seems to fix the issue, but I fear… | |||||
@davidedmundson I can not find a good way to avoid this issue, do you have time to help me a bit? vpilo: @davidedmundson I can not find a good way to avoid this issue, do you have time to help me a… | |||||
51 | /usr/lib/x86_64-linux-gnu/qml/org/kde/kcm/SimpleKCM.qml:117: TypeError: Cannot read property 'atYEnd' of null | ||||
52 | /usr/lib/x86_64-linux-gnu/qml/org/kde/kcm/SimpleKCM.qml:51: TypeError: Cannot read property 'contentHeight' of null | ||||
53 | /usr/lib/x86_64-linux-gnu/qt5/qml/QtQuick/Controls.2/org.kde.desktop/ComboBox.qml:137:29: Unable to assign [undefined] to QQmlComponent | ||||
davidedmundson: that's not your issue, ignore this one. | |||||
54 | */ | ||||
55 | | ||||
56 | namespace | ||||
69 | { | 57 | { | ||
70 | setupUi(this); | 58 | static const QString s_configFile { QStringLiteral("kwinrc") }; | ||
71 | } | 59 | static const QString s_configGroup { QStringLiteral("org.kde.kdecoration2") }; | ||
60 | static const QString s_configPlugin { QStringLiteral("library") }; | ||||
61 | static const QString s_configTheme { QStringLiteral("theme") }; | ||||
62 | static const QString s_configBorderSize { QStringLiteral("BorderSize") }; | ||||
63 | static const QString s_configCloseOnDoubleClickOnMenu { QStringLiteral("CloseOnDoubleClickOnMenu") }; | ||||
Given that this is anonymous namespace you don't need static, also please don't indent contents of the namespace. zzag: Given that this is anonymous namespace you don't need `static`, also please don't indent… | |||||
64 | static const QString s_configDecoButtonsOnLeft { QStringLiteral("ButtonsOnLeft") }; | ||||
65 | static const QString s_configDecoButtonsOnRight { QStringLiteral("ButtonsOnRight") }; | ||||
72 | 66 | | |||
73 | static bool s_loading = false; | 67 | static const KDecoration2::BorderSize s_defaultBorderSize = KDecoration2::BorderSize::Normal; | ||
74 | 68 | static const bool s_defaultCloseOnDoubleClickOnMenu = false; | |||
75 | ConfigurationModule::ConfigurationModule(QWidget *parent, const QVariantList &args) | 69 | | ||
76 | : KCModule(parent, args) | 70 | static const DecorationButtonsList s_defaultDecoButtonsOnLeft { | ||
77 | , m_model(new DecorationsModel(this)) | 71 | KDecoration2::DecorationButtonType::Menu, | ||
78 | , m_proxyModel(new QSortFilterProxyModel(this)) | 72 | KDecoration2::DecorationButtonType::OnAllDesktops | ||
79 | , m_ui(new ConfigurationForm(this)) | 73 | }; | ||
80 | , m_leftButtons(new Preview::ButtonsModel(QVector<DecorationButtonType>(), this)) | 74 | static const DecorationButtonsList s_defaultDecoButtonsOnRight { | ||
81 | , m_rightButtons(new Preview::ButtonsModel(QVector<DecorationButtonType>(), this)) | 75 | KDecoration2::DecorationButtonType::ContextHelp, | ||
82 | , m_availableButtons(new Preview::ButtonsModel(this)) | 76 | KDecoration2::DecorationButtonType::Minimize, | ||
83 | { | 77 | KDecoration2::DecorationButtonType::Maximize, | ||
84 | m_proxyModel->setSourceModel(m_model); | 78 | KDecoration2::DecorationButtonType::Close | ||
85 | m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); | | |||
86 | m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); | | |||
87 | m_proxyModel->sort(0); | | |||
88 | connect(m_ui->filter, &QLineEdit::textChanged, m_proxyModel, &QSortFilterProxyModel::setFilterFixedString); | | |||
89 | | ||||
90 | m_quickView = new QQuickView(0); | | |||
91 | KDeclarative::KDeclarative kdeclarative; | | |||
92 | kdeclarative.setDeclarativeEngine(m_quickView->engine()); | | |||
93 | kdeclarative.setTranslationDomain(QStringLiteral(TRANSLATION_DOMAIN)); | | |||
94 | kdeclarative.setupContext(); | | |||
95 | kdeclarative.setupEngine(m_quickView->engine()); | | |||
96 | | ||||
97 | qmlRegisterType<QAbstractItemModel>(); | | |||
98 | QWidget *widget = QWidget::createWindowContainer(m_quickView, this); | | |||
99 | QVBoxLayout* layout = new QVBoxLayout(m_ui->view); | | |||
100 | layout->setContentsMargins(0,0,0,0); | | |||
101 | layout->addWidget(widget); | | |||
102 | | ||||
103 | m_quickView->rootContext()->setContextProperty(QStringLiteral("decorationsModel"), m_proxyModel); | | |||
104 | updateColors(); | | |||
105 | m_quickView->rootContext()->setContextProperty("_borderSizesIndex", 3); // 3 is normal | | |||
106 | m_quickView->rootContext()->setContextProperty("leftButtons", m_leftButtons); | | |||
107 | m_quickView->rootContext()->setContextProperty("rightButtons", m_rightButtons); | | |||
108 | m_quickView->rootContext()->setContextProperty("availableButtons", m_availableButtons); | | |||
109 | m_quickView->rootContext()->setContextProperty("initialThemeIndex", -1); | | |||
110 | | ||||
111 | m_quickView->rootContext()->setContextProperty("titleFont", QFontDatabase::systemFont(QFontDatabase::TitleFont)); | | |||
112 | m_quickView->setResizeMode(QQuickView::SizeRootObjectToView); | | |||
113 | m_quickView->setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/kcm_kwindecoration/main.qml")))); | | |||
114 | if (m_quickView->status() == QQuickView::Ready) { | | |||
115 | auto listView = m_quickView->rootObject()->findChild<QQuickItem*>("listView"); | | |||
116 | if (listView) { | | |||
117 | connect(listView, SIGNAL(userChangedSelection()), this, SLOT(changed())); | | |||
118 | } | | |||
119 | } | | |||
120 | | ||||
121 | m_ui->tabWidget->tabBar()->disconnect(); | | |||
122 | auto setCurrentTab = [this](int index) { | | |||
123 | if (index == 0) | | |||
124 | m_ui->doubleClickMessage->hide(); | | |||
125 | m_ui->filter->setVisible(index == 0); | | |||
126 | m_ui->knsButton->setVisible(index == 0); | | |||
127 | if (auto themeList = m_quickView->rootObject()->findChild<QQuickItem*>("themeList")) { | | |||
128 | themeList->setVisible(index == 0); | | |||
129 | } | | |||
130 | m_ui->borderSizesLabel->setVisible(index == 0); | | |||
131 | m_ui->borderSizesCombo->setVisible(index == 0); | | |||
132 | | ||||
133 | m_ui->closeWindowsDoubleClick->setVisible(index == 1); | | |||
134 | if (auto buttonLayout = m_quickView->rootObject()->findChild<QQuickItem*>("buttonLayout")) { | | |||
135 | buttonLayout->setVisible(index == 1); | | |||
136 | } | | |||
137 | }; | 79 | }; | ||
138 | connect(m_ui->tabWidget->tabBar(), &QTabBar::currentChanged, this, setCurrentTab); | | |||
139 | setCurrentTab(0); | | |||
140 | 80 | | |||
141 | m_ui->doubleClickMessage->setVisible(false); | 81 | #if HAVE_BREEZE_DECO | ||
142 | m_ui->doubleClickMessage->setText(i18n("Close by double clicking:\n To open the menu, keep the button pressed until it appears.")); | 82 | static const QString s_defaultPlugin { QStringLiteral(BREEZE_KDECORATION_PLUGIN_ID) }; | ||
143 | m_ui->doubleClickMessage->setCloseButtonVisible(true); | 83 | static const QString s_defaultTheme { QStringLiteral("" /* No theme name */) }; | ||
144 | m_ui->borderSizesCombo->setItemData(0, QVariant::fromValue(BorderSize::None)); | 84 | #else | ||
145 | m_ui->borderSizesCombo->setItemData(1, QVariant::fromValue(BorderSize::NoSides)); | 85 | static const QString s_defaultPlugin { QStringLiteral("org.kde.kwin.aurorae") }; | ||
146 | m_ui->borderSizesCombo->setItemData(2, QVariant::fromValue(BorderSize::Tiny)); | 86 | static const QString s_defaultTheme { QStringLiteral("kwin4_decoration_qml_plastik") }; | ||
147 | m_ui->borderSizesCombo->setItemData(3, QVariant::fromValue(BorderSize::Normal)); | 87 | #endif | ||
148 | m_ui->borderSizesCombo->setItemData(4, QVariant::fromValue(BorderSize::Large)); | | |||
149 | m_ui->borderSizesCombo->setItemData(5, QVariant::fromValue(BorderSize::VeryLarge)); | | |||
150 | m_ui->borderSizesCombo->setItemData(6, QVariant::fromValue(BorderSize::Huge)); | | |||
151 | m_ui->borderSizesCombo->setItemData(7, QVariant::fromValue(BorderSize::VeryHuge)); | | |||
152 | m_ui->borderSizesCombo->setItemData(8, QVariant::fromValue(BorderSize::Oversized)); | | |||
153 | m_ui->knsButton->setIcon(QIcon::fromTheme(s_ghnsIcon)); | | |||
154 | | ||||
155 | auto changedSlot = static_cast<void (ConfigurationModule::*)()>(&ConfigurationModule::changed); | | |||
156 | connect(m_ui->closeWindowsDoubleClick, &QCheckBox::stateChanged, this, changedSlot); | | |||
157 | connect(m_ui->closeWindowsDoubleClick, &QCheckBox::toggled, this, | | |||
158 | [this] (bool toggled) { | | |||
159 | if (s_loading) { | | |||
160 | return; | | |||
161 | } | | |||
162 | if (toggled) | | |||
163 | m_ui->doubleClickMessage->animatedShow(); | | |||
164 | else | | |||
165 | m_ui->doubleClickMessage->animatedHide(); | | |||
166 | } | | |||
167 | ); | | |||
168 | connect(m_ui->borderSizesCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), | | |||
169 | this, [this] (int index) { | | |||
170 | auto listView = m_quickView->rootObject()->findChild<QQuickItem*>("listView"); | | |||
171 | if (listView) { | | |||
172 | listView->setProperty("borderSizesIndex", index); | | |||
173 | } | | |||
174 | changed(); | | |||
175 | } | | |||
176 | ); | | |||
177 | connect(m_model, &QAbstractItemModel::modelReset, this, | | |||
178 | [this] { | | |||
179 | const auto &kns = m_model->knsProviders(); | | |||
180 | m_ui->knsButton->setEnabled(!kns.isEmpty()); | | |||
181 | if (kns.isEmpty()) { | | |||
182 | return; | | |||
183 | } | | |||
184 | if (kns.count() > 1) { | | |||
185 | QMenu *menu = new QMenu(m_ui->knsButton); | | |||
186 | for (auto it = kns.begin(); it != kns.end(); ++it) { | | |||
187 | QAction *action = menu->addAction(QIcon::fromTheme(s_ghnsIcon), it.value()); | | |||
188 | action->setData(it.key()); | | |||
189 | connect(action, &QAction::triggered, this, [this, action] { showKNS(action->data().toString());}); | | |||
190 | } | | |||
191 | m_ui->knsButton->setMenu(menu); | | |||
192 | } | 88 | } | ||
89 | | ||||
90 | KCMKWinDecoration::KCMKWinDecoration(QObject *parent, const QVariantList &arguments) | ||||
91 | : KQuickAddons::ConfigModule(parent, arguments) | ||||
92 | , m_themesModel(new KDecoration2::Configuration::DecorationsModel(this)) | ||||
93 | , m_proxyThemesModel(new QSortFilterProxyModel(this)) | ||||
94 | , m_leftButtonsModel(new KDecoration2::Preview::ButtonsModel(DecorationButtonsList(), this)) | ||||
95 | , m_rightButtonsModel(new KDecoration2::Preview::ButtonsModel(DecorationButtonsList(), this)) | ||||
96 | , m_availableButtonsModel(new KDecoration2::Preview::ButtonsModel(this)) | ||||
97 | , m_savedSettings{ s_defaultBorderSize, -2 /* for setTheme() */, false, s_defaultDecoButtonsOnLeft, s_defaultDecoButtonsOnRight } | ||||
98 | , m_currentSettings(m_savedSettings) | ||||
99 | { | ||||
100 | KAboutData* about = new KAboutData(QStringLiteral("kcm_kwindecoration"), | ||||
101 | i18n("Configure Window Decorations"), | ||||
ngraham: The string from the`Comment` in the desktop file needs to be duplicated here. | |||||
102 | QStringLiteral("1.0"), | ||||
103 | QString(), | ||||
104 | KAboutLicense::GPL); | ||||
zzag: Please align it. | |||||
105 | about->addAuthor(i18n("Valerio Pilo"), | ||||
106 | i18n("Author"), | ||||
107 | QStringLiteral("vpilo@coldshock.net")); | ||||
108 | setAboutData(about); | ||||
109 | setButtons(Help | Default | Apply); | ||||
110 | | ||||
111 | qmlRegisterType<QAbstractListModel>(); | ||||
112 | qmlRegisterType<QSortFilterProxyModel>(); | ||||
113 | | ||||
114 | m_proxyThemesModel->setSourceModel(m_themesModel); | ||||
115 | m_proxyThemesModel->setFilterCaseSensitivity(Qt::CaseInsensitive); | ||||
116 | m_proxyThemesModel->setSortCaseSensitivity(Qt::CaseInsensitive); | ||||
117 | m_proxyThemesModel->sort(0); | ||||
118 | | ||||
119 | connect(m_leftButtonsModel, &QAbstractItemModel::rowsInserted, this, &KCMKWinDecoration::updateNeedsSave); | ||||
120 | connect(m_leftButtonsModel, &QAbstractItemModel::rowsMoved, this, &KCMKWinDecoration::updateNeedsSave); | ||||
121 | connect(m_leftButtonsModel, &QAbstractItemModel::rowsRemoved, this, &KCMKWinDecoration::updateNeedsSave); | ||||
122 | connect(m_leftButtonsModel, &QAbstractItemModel::modelReset, this, &KCMKWinDecoration::updateNeedsSave); | ||||
123 | connect(m_rightButtonsModel, &QAbstractItemModel::rowsInserted, this, &KCMKWinDecoration::updateNeedsSave); | ||||
124 | connect(m_rightButtonsModel, &QAbstractItemModel::rowsMoved, this, &KCMKWinDecoration::updateNeedsSave); | ||||
125 | connect(m_rightButtonsModel, &QAbstractItemModel::rowsRemoved, this, &KCMKWinDecoration::updateNeedsSave); | ||||
126 | connect(m_rightButtonsModel, &QAbstractItemModel::modelReset, this, &KCMKWinDecoration::updateNeedsSave); | ||||
127 | | ||||
128 | QMetaObject::invokeMethod(m_themesModel, "init", Qt::QueuedConnection); | ||||
zzag: Use `auto`. | |||||
129 | } | ||||
130 | | ||||
131 | void KCMKWinDecoration::getNewStuff(QQuickItem *context) | ||||
132 | { | ||||
133 | if (!m_newStuffDialog) { | ||||
134 | m_newStuffDialog = new KNS3::DownloadDialog(QStringLiteral("window-decorations.knsrc")); | ||||
135 | m_newStuffDialog->setWindowTitle(i18n("Download New Window Decorations")); | ||||
136 | m_newStuffDialog->setWindowModality(Qt::WindowModal); | ||||
137 | connect(m_newStuffDialog, &KNS3::DownloadDialog::accepted, this, &KCMKWinDecoration::load); | ||||
138 | } | ||||
139 | | ||||
140 | if (context && context->window()) { | ||||
141 | m_newStuffDialog->winId(); // so it creates the windowHandle() | ||||
142 | m_newStuffDialog->windowHandle()->setTransientParent(context->window()); | ||||
193 | } | 143 | } | ||
194 | ); | 144 | /* | ||
195 | connect(m_ui->knsButton, &QPushButton::clicked, this, | 145 | // FIXME dynamically add categories for installed themes? | ||
davidedmundson: Can I check this FIXME is just a new idea and not a regression? | |||||
It is a regression, the older KCM collected from plugins one "KNewStuff" field, and added it to the categories. This seems not to be possible anymore because
I'll just take it away for now. I'd just like a confirmation that the categories I chose for the .knsrc file are correct/acceptable. vpilo: It is a regression, the older KCM collected from plugins one "KNewStuff" field, and added it to… | |||||
196 | [this] { | 146 | // also FIXME verify list of Categories in .knsrc file | ||
197 | const auto &kns = m_model->knsProviders(); | 147 | QStringList cats = m_newStuffDialog->engine()->categories(); | ||
198 | if (kns.isEmpty()) { | 148 | cats.append(m_themesModel->knsProviders()); | ||
199 | return; | 149 | for (auto it = cats.cbegin(); it != cats.cend(); ++it) { | ||
150 | qInfo() << "provider" << *it; | ||||
200 | } | 151 | } | ||
201 | showKNS(kns.firstKey()); | 152 | m_newStuffDialog->engine()->setCategoriesFilter(cats); | ||
153 | */ | ||||
154 | m_newStuffDialog->show(); | ||||
202 | } | 155 | } | ||
203 | ); | | |||
204 | connect(m_leftButtons, &QAbstractItemModel::rowsInserted, this, changedSlot); | | |||
205 | connect(m_leftButtons, &QAbstractItemModel::rowsMoved, this, changedSlot); | | |||
206 | connect(m_leftButtons, &QAbstractItemModel::rowsRemoved, this, changedSlot); | | |||
207 | connect(m_rightButtons, &QAbstractItemModel::rowsInserted, this, changedSlot); | | |||
208 | connect(m_rightButtons, &QAbstractItemModel::rowsMoved, this, changedSlot); | | |||
209 | connect(m_rightButtons, &QAbstractItemModel::rowsRemoved, this, changedSlot); | | |||
210 | 156 | | |||
211 | QVBoxLayout *l = new QVBoxLayout(this); | 157 | void KCMKWinDecoration::load() | ||
212 | l->addWidget(m_ui); | 158 | { | ||
213 | QMetaObject::invokeMethod(m_model, "init", Qt::QueuedConnection); | 159 | const KConfigGroup config = KSharedConfig::openConfig(s_configFile)->group(s_configGroup); | ||
214 | 160 | | |||
215 | m_ui->installEventFilter(this); | 161 | const QString plugin = config.readEntry(s_configPlugin, s_defaultPlugin); | ||
162 | const QString theme = config.readEntry(s_configTheme, s_defaultTheme); | ||||
163 | int themeIndex = m_proxyThemesModel->mapFromSource(m_themesModel->findDecoration(plugin, theme)).row(); | ||||
164 | if (themeIndex < 0) { | ||||
165 | qWarning() << "Plugin" << plugin << "and theme" << theme << "not found"; | ||||
166 | } else { | ||||
167 | qDebug() << "Current theme: plugin" << plugin << "and theme" << theme; | ||||
216 | } | 168 | } | ||
169 | setTheme(themeIndex); | ||||
217 | 170 | | |||
218 | ConfigurationModule::~ConfigurationModule() = default; | 171 | setCloseOnDoubleClickOnMenu(config.readEntry(s_configCloseOnDoubleClickOnMenu, s_defaultCloseOnDoubleClickOnMenu)); | ||
219 | 172 | | |||
220 | void ConfigurationModule::showEvent(QShowEvent *ev) | 173 | const QString defaultSizeName = Utils::borderSizeToString(s_defaultBorderSize); | ||
221 | { | 174 | setBorderSize(Utils::stringToBorderSize(config.readEntry(s_configBorderSize, defaultSizeName))); | ||
222 | KCModule::showEvent(ev); | | |||
223 | } | | |||
224 | 175 | | |||
225 | static const QMap<QString, KDecoration2::BorderSize> s_sizes = QMap<QString, KDecoration2::BorderSize>({ | 176 | m_leftButtonsModel->replace(Utils::readDecorationButtons(config, s_configDecoButtonsOnLeft, s_defaultDecoButtonsOnLeft)); | ||
226 | {QStringLiteral("None"), BorderSize::None}, | 177 | m_rightButtonsModel->replace(Utils::readDecorationButtons(config, s_configDecoButtonsOnRight, s_defaultDecoButtonsOnRight)); | ||
227 | {QStringLiteral("NoSides"), BorderSize::NoSides}, | 178 | m_currentSettings.buttonsOnLeft = m_leftButtonsModel->buttons(); | ||
228 | {QStringLiteral("Tiny"), BorderSize::Tiny}, | 179 | m_currentSettings.buttonsOnRight = m_rightButtonsModel->buttons(); | ||
229 | {s_borderSizeNormal, BorderSize::Normal}, | | |||
230 | {QStringLiteral("Large"), BorderSize::Large}, | | |||
231 | {QStringLiteral("VeryLarge"), BorderSize::VeryLarge}, | | |||
232 | {QStringLiteral("Huge"), BorderSize::Huge}, | | |||
233 | {QStringLiteral("VeryHuge"), BorderSize::VeryHuge}, | | |||
234 | {QStringLiteral("Oversized"), BorderSize::Oversized} | | |||
235 | }); | | |||
236 | 180 | | |||
237 | static BorderSize stringToSize(const QString &name) | 181 | m_savedSettings = m_currentSettings; | ||
238 | { | | |||
239 | auto it = s_sizes.constFind(name); | | |||
240 | if (it == s_sizes.constEnd()) { | | |||
241 | // non sense values are interpreted just like normal | | |||
242 | return BorderSize::Normal; | | |||
243 | } | | |||
244 | return it.value(); | | |||
245 | } | | |||
246 | 182 | | |||
247 | static QString sizeToString(BorderSize size) | 183 | updateNeedsSave(); | ||
248 | { | | |||
249 | return s_sizes.key(size, s_borderSizeNormal); | | |||
250 | } | 184 | } | ||
251 | 185 | | |||
252 | static QHash<KDecoration2::DecorationButtonType, QChar> s_buttonNames; | 186 | void KCMKWinDecoration::save() | ||
253 | static void initButtons() | | |||
254 | { | 187 | { | ||
255 | if (!s_buttonNames.isEmpty()) { | 188 | KConfigGroup config = KSharedConfig::openConfig(s_configFile)->group(s_configGroup); | ||
256 | return; | 189 | | ||
257 | } | 190 | if (m_currentSettings.themeIndex >= 0) { | ||
258 | s_buttonNames[KDecoration2::DecorationButtonType::Menu] = QChar('M'); | 191 | const QModelIndex index = m_proxyThemesModel->index(m_currentSettings.themeIndex, 0); | ||
259 | s_buttonNames[KDecoration2::DecorationButtonType::ApplicationMenu] = QChar('N'); | 192 | if (index.isValid()) { | ||
260 | s_buttonNames[KDecoration2::DecorationButtonType::OnAllDesktops] = QChar('S'); | 193 | const QString plugin = index.data(KDecoration2::Configuration::DecorationsModel::PluginNameRole).toString(); | ||
261 | s_buttonNames[KDecoration2::DecorationButtonType::ContextHelp] = QChar('H'); | 194 | const QString theme = index.data(KDecoration2::Configuration::DecorationsModel::ThemeNameRole).toString(); | ||
262 | s_buttonNames[KDecoration2::DecorationButtonType::Minimize] = QChar('I'); | 195 | config.writeEntry(s_configPlugin, plugin); | ||
263 | s_buttonNames[KDecoration2::DecorationButtonType::Maximize] = QChar('A'); | 196 | config.writeEntry(s_configTheme, theme); | ||
264 | s_buttonNames[KDecoration2::DecorationButtonType::Close] = QChar('X'); | 197 | qDebug() << "Saved theme: plugin" << plugin << "and theme" << theme; | ||
265 | s_buttonNames[KDecoration2::DecorationButtonType::KeepAbove] = QChar('F'); | 198 | } else { | ||
266 | s_buttonNames[KDecoration2::DecorationButtonType::KeepBelow] = QChar('B'); | 199 | qWarning() << "Cannot match theme index" << m_currentSettings.themeIndex << "in model"; | ||
267 | s_buttonNames[KDecoration2::DecorationButtonType::Shade] = QChar('L'); | | |||
268 | } | | |||
269 | | ||||
270 | static QString buttonsToString(const QVector<KDecoration2::DecorationButtonType> &buttons) | | |||
271 | { | | |||
272 | auto buttonToString = [](KDecoration2::DecorationButtonType button) -> QChar { | | |||
273 | const auto it = s_buttonNames.constFind(button); | | |||
274 | if (it != s_buttonNames.constEnd()) { | | |||
275 | return it.value(); | | |||
276 | } | 200 | } | ||
277 | return QChar(); | | |||
278 | }; | | |||
279 | QString ret; | | |||
280 | for (auto button : buttons) { | | |||
281 | ret.append(buttonToString(button)); | | |||
282 | } | 201 | } | ||
283 | return ret; | 202 | | ||
203 | config.writeEntry(s_configCloseOnDoubleClickOnMenu, m_currentSettings.closeOnDoubleClickOnMenu); | ||||
204 | config.writeEntry(s_configBorderSize, Utils::borderSizeToString(m_currentSettings.borderSize)); | ||||
205 | config.writeEntry(s_configDecoButtonsOnLeft, Utils::buttonsToString(m_currentSettings.buttonsOnLeft)); | ||||
206 | config.writeEntry(s_configDecoButtonsOnRight, Utils::buttonsToString(m_currentSettings.buttonsOnRight)); | ||||
207 | config.sync(); | ||||
208 | | ||||
209 | m_savedSettings = m_currentSettings; | ||||
210 | | ||||
211 | // Send a signal to all kwin instances | ||||
212 | QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), | ||||
213 | QStringLiteral("org.kde.KWin"), | ||||
214 | QStringLiteral("reloadConfig")); | ||||
215 | QDBusConnection::sessionBus().send(message); | ||||
216 | | ||||
217 | updateNeedsSave(); | ||||
284 | } | 218 | } | ||
285 | 219 | | |||
286 | static | 220 | void KCMKWinDecoration::defaults() | ||
287 | QVector< KDecoration2::DecorationButtonType > readDecorationButtons(const KConfigGroup &config, | | |||
288 | const char *key, | | |||
289 | const QVector< KDecoration2::DecorationButtonType > &defaultValue) | | |||
290 | { | 221 | { | ||
291 | initButtons(); | 222 | // FIXME Check why the "Defaults" button gets never enabled neither in kcmshell nor systemsettings | ||
There's a typo in the QML, it is KCM.ConfigModule.Default (without an "s"). However, you seem to be doing it from both QML and C++ (setButtons()), one is sufficient. (I have seen this mistake in other QML KCMs, I assume you just copied that over) broulik: There's a typo in the QML, it is `KCM.ConfigModule.Default` (without an "s").
However, you… | |||||
292 | auto buttonsFromString = [](const QString &buttons) -> QVector<KDecoration2::DecorationButtonType> { | 223 | | ||
293 | QVector<KDecoration2::DecorationButtonType> ret; | 224 | int themeIndex = m_proxyThemesModel->mapFromSource(m_themesModel->findDecoration(s_defaultPlugin, s_defaultTheme)).row(); | ||
294 | for (auto it = buttons.begin(); it != buttons.end(); ++it) { | 225 | if (themeIndex < 0) { | ||
295 | for (auto it2 = s_buttonNames.constBegin(); it2 != s_buttonNames.constEnd(); ++it2) { | 226 | qWarning() << "Default plugin" << s_defaultPlugin << "and theme" << s_defaultTheme << "not found"; | ||
296 | if (it2.value() == (*it)) { | | |||
297 | ret << it2.key(); | | |||
298 | } | | |||
299 | } | | |||
300 | } | | |||
301 | return ret; | | |||
302 | }; | | |||
303 | return buttonsFromString(config.readEntry(key, buttonsToString(defaultValue))); | | |||
304 | } | 227 | } | ||
228 | setTheme(themeIndex); | ||||
305 | 229 | | |||
306 | void ConfigurationModule::load() | 230 | setCloseOnDoubleClickOnMenu(s_defaultCloseOnDoubleClickOnMenu); | ||
307 | { | | |||
308 | s_loading = true; | | |||
309 | const KConfigGroup config = KSharedConfig::openConfig("kwinrc")->group(s_pluginName); | | |||
310 | const QString plugin = config.readEntry("library", s_defaultPlugin); | | |||
311 | const QString theme = config.readEntry("theme", s_defaultTheme); | | |||
312 | m_ui->closeWindowsDoubleClick->setChecked(config.readEntry("CloseOnDoubleClickOnMenu", false)); | | |||
313 | const QVariant border = QVariant::fromValue(stringToSize(config.readEntry("BorderSize", s_borderSizeNormal))); | | |||
314 | m_ui->borderSizesCombo->setCurrentIndex(m_ui->borderSizesCombo->findData(border)); | | |||
315 | 231 | | |||
316 | int themeIndex = m_proxyModel->mapFromSource(m_model->findDecoration(plugin, theme)).row(); | 232 | m_leftButtonsModel->replace(s_defaultDecoButtonsOnLeft); | ||
317 | m_quickView->rootContext()->setContextProperty("initialThemeIndex", themeIndex); | 233 | m_rightButtonsModel->replace(s_defaultDecoButtonsOnRight); | ||
318 | 234 | | |||
319 | // buttons | 235 | updateNeedsSave(); | ||
320 | const auto &left = readDecorationButtons(config, "ButtonsOnLeft", QVector<KDecoration2::DecorationButtonType >{ | | |||
321 | KDecoration2::DecorationButtonType::Menu, | | |||
322 | KDecoration2::DecorationButtonType::OnAllDesktops | | |||
323 | }); | | |||
324 | while (m_leftButtons->rowCount() > 0) { | | |||
325 | m_leftButtons->remove(0); | | |||
326 | } | | |||
327 | for (auto it = left.begin(); it != left.end(); ++it) { | | |||
328 | m_leftButtons->add(*it); | | |||
329 | } | | |||
330 | const auto &right = readDecorationButtons(config, "ButtonsOnRight", QVector<KDecoration2::DecorationButtonType >{ | | |||
331 | KDecoration2::DecorationButtonType::ContextHelp, | | |||
332 | KDecoration2::DecorationButtonType::Minimize, | | |||
333 | KDecoration2::DecorationButtonType::Maximize, | | |||
334 | KDecoration2::DecorationButtonType::Close | | |||
335 | }); | | |||
336 | while (m_rightButtons->rowCount() > 0) { | | |||
337 | m_rightButtons->remove(0); | | |||
338 | } | | |||
339 | for (auto it = right.begin(); it != right.end(); ++it) { | | |||
340 | m_rightButtons->add(*it); | | |||
341 | } | 236 | } | ||
342 | 237 | | |||
343 | KCModule::load(); | 238 | void KCMKWinDecoration::updateNeedsSave() | ||
344 | s_loading = false; | 239 | { | ||
240 | m_currentSettings.buttonsOnLeft = m_leftButtonsModel->buttons(); | ||||
241 | m_currentSettings.buttonsOnRight = m_rightButtonsModel->buttons(); | ||||
242 | | ||||
243 | setNeedsSave(m_savedSettings.closeOnDoubleClickOnMenu != m_currentSettings.closeOnDoubleClickOnMenu | ||||
244 | || m_savedSettings.borderSize != m_currentSettings.borderSize | ||||
245 | || m_savedSettings.themeIndex != m_currentSettings.themeIndex | ||||
246 | || m_savedSettings.buttonsOnLeft != m_currentSettings.buttonsOnLeft | ||||
247 | || m_savedSettings.buttonsOnRight != m_currentSettings.buttonsOnRight); | ||||
345 | } | 248 | } | ||
346 | 249 | | |||
347 | void ConfigurationModule::save() | 250 | QSortFilterProxyModel *KCMKWinDecoration::themesModel() const | ||
348 | { | 251 | { | ||
349 | KConfigGroup config = KSharedConfig::openConfig("kwinrc")->group(s_pluginName); | 252 | return m_proxyThemesModel; | ||
350 | config.writeEntry("CloseOnDoubleClickOnMenu", m_ui->closeWindowsDoubleClick->isChecked()); | | |||
351 | config.writeEntry("BorderSize", sizeToString(m_ui->borderSizesCombo->currentData().value<BorderSize>())); | | |||
352 | if (auto listView = m_quickView->rootObject()->findChild<QQuickItem*>("listView")) { | | |||
353 | const int currentIndex = listView->property("currentIndex").toInt(); | | |||
354 | if (currentIndex != -1) { | | |||
355 | const QModelIndex index = m_proxyModel->index(currentIndex, 0); | | |||
356 | if (index.isValid()) { | | |||
357 | config.writeEntry("library", index.data(Qt::UserRole + 4).toString()); | | |||
358 | const QString theme = index.data(Qt::UserRole +5).toString(); | | |||
359 | config.writeEntry("theme", theme); | | |||
360 | } | | |||
361 | } | | |||
362 | } | 253 | } | ||
363 | config.writeEntry("ButtonsOnLeft", buttonsToString(m_leftButtons->buttons())); | 254 | | ||
364 | config.writeEntry("ButtonsOnRight", buttonsToString(m_rightButtons->buttons())); | 255 | QAbstractListModel *KCMKWinDecoration::leftButtonsModel() | ||
365 | config.sync(); | 256 | { | ||
366 | KCModule::save(); | 257 | return m_leftButtonsModel; | ||
367 | // Send signal to all kwin instances | | |||
368 | QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), | | |||
369 | QStringLiteral("org.kde.KWin"), | | |||
370 | QStringLiteral("reloadConfig")); | | |||
371 | QDBusConnection::sessionBus().send(message); | | |||
372 | } | 258 | } | ||
373 | 259 | | |||
374 | void ConfigurationModule::defaults() | 260 | QAbstractListModel *KCMKWinDecoration::rightButtonsModel() | ||
375 | { | 261 | { | ||
376 | if (auto listView = m_quickView->rootObject()->findChild<QQuickItem*>("listView")) { | 262 | return m_rightButtonsModel; | ||
377 | const QModelIndex index = m_proxyModel->mapFromSource(m_model->findDecoration(s_defaultPlugin)); | | |||
378 | listView->setProperty("currentIndex", index.isValid() ? index.row() : -1); | | |||
379 | } | | |||
380 | m_ui->borderSizesCombo->setCurrentIndex(m_ui->borderSizesCombo->findData(QVariant::fromValue(stringToSize(s_borderSizeNormal)))); | | |||
381 | m_ui->closeWindowsDoubleClick->setChecked(false); | | |||
382 | KCModule::defaults(); | | |||
383 | } | | |||
384 | | ||||
385 | void ConfigurationModule::showKNS(const QString &config) | | |||
386 | { | | |||
387 | QPointer<KNS3::DownloadDialog> downloadDialog = new KNS3::DownloadDialog(config, this); | | |||
388 | if (downloadDialog->exec() == QDialog::Accepted && !downloadDialog->changedEntries().isEmpty()) { | | |||
389 | auto listView = m_quickView->rootObject()->findChild<QQuickItem*>("listView"); | | |||
390 | QString selectedPluginName; | | |||
391 | QString selectedThemeName; | | |||
392 | if (listView) { | | |||
393 | const QModelIndex index = m_proxyModel->index(listView->property("currentIndex").toInt(), 0); | | |||
394 | if (index.isValid()) { | | |||
395 | selectedPluginName = index.data(Qt::UserRole + 4).toString(); | | |||
396 | selectedThemeName = index.data(Qt::UserRole + 5).toString(); | | |||
397 | } | 263 | } | ||
264 | | ||||
265 | QAbstractListModel *KCMKWinDecoration::availableButtonsModel() const | ||||
266 | { | ||||
267 | return m_availableButtonsModel; | ||||
398 | } | 268 | } | ||
399 | m_model->init(); | 269 | | ||
400 | if (!selectedPluginName.isEmpty()) { | 270 | QStringList KCMKWinDecoration::borderSizesModel() const | ||
401 | const QModelIndex index = m_model->findDecoration(selectedPluginName, selectedThemeName); | 271 | { | ||
402 | const QModelIndex proxyIndex = m_proxyModel->mapFromSource(index); | 272 | return Utils::getBorderSizeNames().values(); | ||
403 | if (listView) { | | |||
404 | listView->setProperty("currentIndex", proxyIndex.isValid() ? proxyIndex.row() : -1); | | |||
405 | } | 273 | } | ||
274 | | ||||
275 | int KCMKWinDecoration::borderSize() const | ||||
276 | { | ||||
277 | return Utils::getBorderSizeNames().keys().indexOf(m_currentSettings.borderSize); | ||||
406 | } | 278 | } | ||
279 | | ||||
280 | int KCMKWinDecoration::theme() const | ||||
281 | { | ||||
282 | return m_currentSettings.themeIndex; | ||||
407 | } | 283 | } | ||
408 | delete downloadDialog; | 284 | | ||
285 | bool KCMKWinDecoration::closeOnDoubleClickOnMenu() const | ||||
286 | { | ||||
287 | return m_currentSettings.closeOnDoubleClickOnMenu; | ||||
409 | } | 288 | } | ||
410 | 289 | | |||
411 | bool ConfigurationModule::eventFilter(QObject *watched, QEvent *e) | 290 | void KCMKWinDecoration::setBorderSize(int index) | ||
412 | { | 291 | { | ||
413 | if (watched != m_ui) { | 292 | setBorderSize(Utils::getBorderSizeNames().keys().at(index)); | ||
414 | return false; | | |||
415 | } | 293 | } | ||
416 | if (e->type() == QEvent::PaletteChange) { | 294 | | ||
417 | updateColors(); | 295 | void KCMKWinDecoration::setBorderSize(KDecoration2::BorderSize size) | ||
296 | { | ||||
297 | if (m_currentSettings.borderSize == size) { | ||||
298 | return; | ||||
418 | } | 299 | } | ||
419 | return false; | 300 | m_currentSettings.borderSize = size; | ||
301 | emit borderSizeChanged(); | ||||
302 | updateNeedsSave(); | ||||
420 | } | 303 | } | ||
421 | 304 | | |||
422 | void ConfigurationModule::updateColors() | 305 | void KCMKWinDecoration::setTheme(int index) | ||
423 | { | 306 | { | ||
424 | m_quickView->rootContext()->setContextProperty("backgroundColor", m_ui->palette().color(QPalette::Active, QPalette::Window)); | 307 | // The initial themeIndex is set to -2 to always initially apply a theme, any theme | ||
425 | m_quickView->rootContext()->setContextProperty("highlightColor", m_ui->palette().color(QPalette::Active, QPalette::Shadow)); | 308 | if (m_currentSettings.themeIndex == index) { | ||
426 | m_quickView->rootContext()->setContextProperty("baseColor", m_ui->palette().color(QPalette::Active, QPalette::Base)); | 309 | return; | ||
310 | } | ||||
311 | m_currentSettings.themeIndex = index; | ||||
312 | emit themeChanged(); | ||||
313 | updateNeedsSave(); | ||||
427 | } | 314 | } | ||
428 | 315 | | |||
316 | void KCMKWinDecoration::setCloseOnDoubleClickOnMenu(bool enable) | ||||
317 | { | ||||
318 | if (m_currentSettings.closeOnDoubleClickOnMenu == enable) { | ||||
319 | return; | ||||
429 | } | 320 | } | ||
321 | m_currentSettings.closeOnDoubleClickOnMenu = enable; | ||||
322 | emit closeOnDoubleClickOnMenuChanged(); | ||||
323 | updateNeedsSave(); | ||||
430 | } | 324 | } | ||
431 | 325 | | |||
432 | #include "kcm.moc" | 326 | #include "kcm.moc" |
See next comment