1 | /* |
2 | SPDX-FileCopyrightText: 2021 Nicolas Fella <nicolas.fella@gmx.de> |
3 | SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #include "kpluginwidget.h" |
9 | #include "kcmoduleloader.h" |
10 | #include "kpluginproxymodel.h" |
11 | #include "kpluginwidget_p.h" |
12 | |
13 | #include <kcmutils_debug.h> |
14 | |
15 | #include <QApplication> |
16 | #include <QCheckBox> |
17 | #include <QDialog> |
18 | #include <QDialogButtonBox> |
19 | #include <QDir> |
20 | #include <QLineEdit> |
21 | #include <QPainter> |
22 | #include <QPushButton> |
23 | #include <QSortFilterProxyModel> |
24 | #include <QStandardPaths> |
25 | #include <QStyle> |
26 | #include <QStyleOptionViewItem> |
27 | #include <QVBoxLayout> |
28 | |
29 | #include <KAboutPluginDialog> |
30 | #include <KCategorizedSortFilterProxyModel> |
31 | #include <KCategorizedView> |
32 | #include <KCategoryDrawer> |
33 | #include <KLocalizedString> |
34 | #include <KPluginMetaData> |
35 | #include <KStandardGuiItem> |
36 | #include <utility> |
37 | |
38 | static constexpr int s_margin = 5; |
39 | |
40 | int KPluginWidgetPrivate::dependantLayoutValue(int value, int width, int totalWidth) const |
41 | { |
42 | if (listView->layoutDirection() == Qt::LeftToRight) { |
43 | return value; |
44 | } |
45 | |
46 | return totalWidth - width - value; |
47 | } |
48 | |
49 | KPluginWidget::KPluginWidget(QWidget *parent) |
50 | : QWidget(parent) |
51 | , d(new KPluginWidgetPrivate) |
52 | { |
53 | auto layout = new QVBoxLayout(this); |
54 | layout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0); |
55 | layout->setSpacing(0); |
56 | |
57 | // Adding content margins on a QLineEdit breaks inline actions |
58 | auto lineEditWrapper = new QWidget(this); |
59 | auto lineEditWrapperLayout = new QVBoxLayout(lineEditWrapper); |
60 | lineEditWrapperLayout->setContentsMargins(left: style()->pixelMetric(metric: QStyle::PM_LayoutLeftMargin), |
61 | top: style()->pixelMetric(metric: QStyle::PM_LayoutTopMargin), |
62 | right: style()->pixelMetric(metric: QStyle::PM_LayoutRightMargin), |
63 | bottom: style()->pixelMetric(metric: QStyle::PM_LayoutBottomMargin)); |
64 | |
65 | d->lineEdit = new QLineEdit(lineEditWrapper); |
66 | d->lineEdit->setClearButtonEnabled(true); |
67 | d->lineEdit->setPlaceholderText(i18n("Search..." )); |
68 | lineEditWrapperLayout->addWidget(d->lineEdit); |
69 | d->listView = new KCategorizedView(this); |
70 | d->listView->setProperty(name: "_breeze_borders_sides" , value: QVariant::fromValue(value: QFlags{Qt::TopEdge})); |
71 | d->categoryDrawer = new KCategoryDrawer(d->listView); |
72 | d->listView->setVerticalScrollMode(QListView::ScrollPerPixel); |
73 | d->listView->setAlternatingRowColors(true); |
74 | d->listView->setCategoryDrawer(d->categoryDrawer); |
75 | |
76 | d->pluginModel = new KPluginModel(this); |
77 | |
78 | connect(sender: d->pluginModel, signal: &KPluginModel::defaulted, context: this, slot: &KPluginWidget::defaulted); |
79 | connect(sender: d->pluginModel, |
80 | signal: &QAbstractItemModel::dataChanged, |
81 | context: this, |
82 | slot: [this](const QModelIndex &topLeft, const QModelIndex & /*bottomRight*/, const QList<int> &roles) { |
83 | if (roles.contains(t: KPluginModel::EnabledRole)) { |
84 | Q_EMIT pluginEnabledChanged(pluginId: topLeft.data(arole: KPluginModel::IdRole).toString(), enabled: topLeft.data(arole: KPluginModel::EnabledRole).toBool()); |
85 | Q_EMIT changed(enabled: d->pluginModel->isSaveNeeded()); |
86 | } |
87 | }); |
88 | |
89 | d->proxyModel = new KPluginProxyModel(this); |
90 | d->proxyModel->setModel(d->pluginModel); |
91 | d->listView->setModel(d->proxyModel); |
92 | d->listView->setAlternatingRowColors(true); |
93 | |
94 | auto pluginDelegate = new PluginDelegate(d.get(), this); |
95 | d->listView->setItemDelegate(pluginDelegate); |
96 | |
97 | d->listView->setMouseTracking(true); |
98 | d->listView->viewport()->setAttribute(Qt::WA_Hover); |
99 | |
100 | connect(sender: d->lineEdit, signal: &QLineEdit::textChanged, context: d->proxyModel, slot: [this](const QString &query) { |
101 | d->proxyModel->setProperty(name: "query" , value: query); |
102 | d->proxyModel->invalidate(); |
103 | }); |
104 | connect(sender: pluginDelegate, signal: &PluginDelegate::configCommitted, context: this, slot: &KPluginWidget::pluginConfigSaved); |
105 | connect(sender: pluginDelegate, signal: &PluginDelegate::changed, context: this, slot: &KPluginWidget::pluginEnabledChanged); |
106 | |
107 | layout->addWidget(lineEditWrapper); |
108 | layout->addWidget(d->listView); |
109 | |
110 | // When a KPluginWidget instance gets focus, |
111 | // it should pass over the focus to its child searchbar. |
112 | setFocusProxy(d->lineEdit); |
113 | } |
114 | |
115 | KPluginWidget::~KPluginWidget() |
116 | { |
117 | delete d->listView->itemDelegate(); |
118 | delete d->listView; // depends on some other things in d, make sure this dies first. |
119 | } |
120 | |
121 | void KPluginWidget::addPlugins(const QList<KPluginMetaData> &plugins, const QString &categoryLabel) |
122 | { |
123 | d->pluginModel->addPlugins(plugins, categoryLabel); |
124 | d->proxyModel->sort(column: 0); |
125 | } |
126 | |
127 | void KPluginWidget::setConfig(const KConfigGroup &config) |
128 | { |
129 | d->pluginModel->setConfig(config); |
130 | } |
131 | |
132 | void KPluginWidget::clear() |
133 | { |
134 | d->pluginModel->clear(); |
135 | } |
136 | |
137 | void KPluginWidget::save() |
138 | { |
139 | d->pluginModel->save(); |
140 | } |
141 | |
142 | void KPluginWidget::load() |
143 | { |
144 | d->pluginModel->load(); |
145 | } |
146 | |
147 | void KPluginWidget::defaults() |
148 | { |
149 | d->pluginModel->defaults(); |
150 | } |
151 | |
152 | bool KPluginWidget::isDefault() const |
153 | { |
154 | for (int i = 0, count = d->pluginModel->rowCount(); i < count; ++i) { |
155 | const QModelIndex index = d->pluginModel->index(row: i, column: 0); |
156 | if (d->pluginModel->data(index, role: Qt::CheckStateRole).toBool() != d->pluginModel->data(index, role: KPluginModel::EnabledByDefaultRole).toBool()) { |
157 | return false; |
158 | } |
159 | } |
160 | |
161 | return true; |
162 | } |
163 | |
164 | bool KPluginWidget::isSaveNeeded() const |
165 | { |
166 | return d->pluginModel->isSaveNeeded(); |
167 | } |
168 | |
169 | void KPluginWidget::setConfigurationArguments(const QVariantList &arguments) |
170 | { |
171 | d->kcmArguments = arguments; |
172 | } |
173 | |
174 | QVariantList KPluginWidget::configurationArguments() const |
175 | { |
176 | return d->kcmArguments; |
177 | } |
178 | |
179 | void KPluginWidget::showConfiguration(const QString &pluginId) |
180 | { |
181 | QModelIndex idx; |
182 | for (int i = 0, c = d->proxyModel->rowCount(); i < c; ++i) { |
183 | const auto currentIndex = d->proxyModel->index(row: i, column: 0); |
184 | const QString id = currentIndex.data(arole: KPluginModel::IdRole).toString(); |
185 | if (id == pluginId) { |
186 | idx = currentIndex; |
187 | break; |
188 | } |
189 | } |
190 | |
191 | if (idx.isValid()) { |
192 | auto delegate = static_cast<PluginDelegate *>(d->listView->itemDelegate()); |
193 | delegate->configure(idx); |
194 | } else { |
195 | qCWarning(KCMUTILS_LOG) << "Could not find plugin" << pluginId; |
196 | } |
197 | } |
198 | |
199 | void KPluginWidget::setDefaultsIndicatorsVisible(bool isVisible) |
200 | { |
201 | auto delegate = static_cast<PluginDelegate *>(d->listView->itemDelegate()); |
202 | delegate->resetModel(); |
203 | |
204 | d->showDefaultIndicator = isVisible; |
205 | } |
206 | |
207 | void KPluginWidget::setAdditionalButtonHandler(const std::function<QPushButton *(const KPluginMetaData &)> &handler) |
208 | { |
209 | auto delegate = static_cast<PluginDelegate *>(d->listView->itemDelegate()); |
210 | delegate->handler = handler; |
211 | } |
212 | |
213 | PluginDelegate::PluginDelegate(KPluginWidgetPrivate *pluginSelector_d_ptr, QObject *parent) |
214 | : KWidgetItemDelegate(pluginSelector_d_ptr->listView, parent) |
215 | , checkBox(new QCheckBox) |
216 | , pushButton(new QPushButton) |
217 | , pluginSelector_d(pluginSelector_d_ptr) |
218 | { |
219 | // set the icon to make sure the size can be properly calculated |
220 | pushButton->setIcon(QIcon::fromTheme(QStringLiteral("configure" ))); |
221 | } |
222 | |
223 | PluginDelegate::~PluginDelegate() |
224 | { |
225 | delete checkBox; |
226 | delete pushButton; |
227 | } |
228 | |
229 | void PluginDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const |
230 | { |
231 | if (!index.isValid()) { |
232 | return; |
233 | } |
234 | |
235 | const int xOffset = checkBox->sizeHint().width(); |
236 | const bool disabled = !index.model()->data(index, role: KPluginModel::IsChangeableRole).toBool(); |
237 | |
238 | painter->save(); |
239 | |
240 | QApplication::style()->drawPrimitive(pe: QStyle::PE_PanelItemViewItem, opt: &option, p: painter, w: nullptr); |
241 | |
242 | const int iconSize = option.rect.height() - (s_margin * 2); |
243 | QIcon icon = QIcon::fromTheme(name: index.model()->data(index, role: Qt::DecorationRole).toString()); |
244 | icon.paint(painter, |
245 | rect: QRect(pluginSelector_d->dependantLayoutValue(value: s_margin + option.rect.left() + xOffset, width: iconSize, totalWidth: option.rect.width()), |
246 | s_margin + option.rect.top(), |
247 | iconSize, |
248 | iconSize)); |
249 | |
250 | QRect contentsRect(pluginSelector_d->dependantLayoutValue(value: s_margin * 2 + iconSize + option.rect.left() + xOffset, |
251 | width: option.rect.width() - (s_margin * 3) - iconSize - xOffset, |
252 | totalWidth: option.rect.width()), |
253 | s_margin + option.rect.top(), |
254 | option.rect.width() - (s_margin * 3) - iconSize - xOffset, |
255 | option.rect.height() - (s_margin * 2)); |
256 | |
257 | int lessHorizontalSpace = s_margin * 2 + pushButton->sizeHint().width(); |
258 | if (index.model()->data(index, role: KPluginModel::ConfigRole).value<KPluginMetaData>().isValid()) { |
259 | lessHorizontalSpace += s_margin + pushButton->sizeHint().width(); |
260 | } |
261 | // Reserve space for extra button |
262 | if (handler) { |
263 | lessHorizontalSpace += s_margin + pushButton->sizeHint().width(); |
264 | } |
265 | |
266 | contentsRect.setWidth(contentsRect.width() - lessHorizontalSpace); |
267 | |
268 | if (option.state & QStyle::State_Selected) { |
269 | painter->setPen(option.palette.highlightedText().color()); |
270 | } |
271 | |
272 | if (pluginSelector_d->listView->layoutDirection() == Qt::RightToLeft) { |
273 | contentsRect.translate(dx: lessHorizontalSpace, dy: 0); |
274 | } |
275 | |
276 | painter->save(); |
277 | if (disabled) { |
278 | QPalette pal(option.palette); |
279 | pal.setCurrentColorGroup(QPalette::Disabled); |
280 | painter->setPen(pal.text().color()); |
281 | } |
282 | |
283 | painter->save(); |
284 | QFont font = titleFont(baseFont: option.font); |
285 | QFontMetrics fmTitle(font); |
286 | painter->setFont(font); |
287 | painter->drawText(r: contentsRect, |
288 | flags: Qt::AlignLeft | Qt::AlignTop, |
289 | text: fmTitle.elidedText(text: index.model()->data(index, role: Qt::DisplayRole).toString(), mode: Qt::ElideRight, width: contentsRect.width())); |
290 | painter->restore(); |
291 | |
292 | painter->drawText( |
293 | r: contentsRect, |
294 | flags: Qt::AlignLeft | Qt::AlignBottom, |
295 | text: option.fontMetrics.elidedText(text: index.model()->data(index, role: KPluginModel::DescriptionRole).toString(), mode: Qt::ElideRight, width: contentsRect.width())); |
296 | |
297 | painter->restore(); |
298 | painter->restore(); |
299 | } |
300 | |
301 | QSize PluginDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const |
302 | { |
303 | int i = 5; |
304 | int j = 1; |
305 | if (index.model()->data(index, role: KPluginModel::ConfigRole).value<KPluginMetaData>().isValid()) { |
306 | i = 6; |
307 | j = 2; |
308 | } |
309 | // Reserve space for extra button |
310 | if (handler) { |
311 | ++j; |
312 | } |
313 | |
314 | const QFont font = titleFont(baseFont: option.font); |
315 | const QFontMetrics fmTitle(font); |
316 | const QString text = index.model()->data(index, role: Qt::DisplayRole).toString(); |
317 | const QString = index.model()->data(index, role: KPluginModel::DescriptionRole).toString(); |
318 | const int maxTextWidth = qMax(a: fmTitle.boundingRect(text).width(), b: option.fontMetrics.boundingRect(text: comment).width()); |
319 | |
320 | const auto iconSize = pluginSelector_d->listView->style()->pixelMetric(metric: QStyle::PM_IconViewIconSize); |
321 | return QSize(maxTextWidth + iconSize + s_margin * i + pushButton->sizeHint().width() * j, |
322 | qMax(a: iconSize + s_margin * 2, b: fmTitle.height() + option.fontMetrics.height() + s_margin * 2)); |
323 | } |
324 | |
325 | QList<QWidget *> PluginDelegate::createItemWidgets(const QModelIndex &index) const |
326 | { |
327 | Q_UNUSED(index); |
328 | QList<QWidget *> widgetList; |
329 | |
330 | auto enabledCheckBox = new QCheckBox; |
331 | connect(sender: enabledCheckBox, signal: &QAbstractButton::clicked, context: this, slot: &PluginDelegate::slotStateChanged); |
332 | |
333 | auto aboutPushButton = new QPushButton; |
334 | aboutPushButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information" ))); |
335 | aboutPushButton->setToolTip(i18n("About" )); |
336 | connect(sender: aboutPushButton, signal: &QAbstractButton::clicked, context: this, slot: &PluginDelegate::slotAboutClicked); |
337 | |
338 | auto configurePushButton = new QPushButton; |
339 | configurePushButton->setIcon(QIcon::fromTheme(QStringLiteral("configure" ))); |
340 | configurePushButton->setToolTip(i18n("Configure" )); |
341 | connect(sender: configurePushButton, signal: &QAbstractButton::clicked, context: this, slot: &PluginDelegate::slotConfigureClicked); |
342 | |
343 | const static QList<QEvent::Type> blockedEvents{ |
344 | QEvent::MouseButtonPress, |
345 | QEvent::MouseButtonRelease, |
346 | QEvent::MouseButtonDblClick, |
347 | QEvent::KeyPress, |
348 | QEvent::KeyRelease, |
349 | }; |
350 | setBlockedEventTypes(widget: enabledCheckBox, types: blockedEvents); |
351 | |
352 | setBlockedEventTypes(widget: aboutPushButton, types: blockedEvents); |
353 | |
354 | setBlockedEventTypes(widget: configurePushButton, types: blockedEvents); |
355 | |
356 | widgetList << enabledCheckBox << aboutPushButton << configurePushButton; |
357 | if (handler) { |
358 | QPushButton *btn = handler(pluginSelector_d->pluginModel->data(index, role: KPluginModel::MetaDataRole).value<KPluginMetaData>()); |
359 | if (btn) { |
360 | widgetList << btn; |
361 | } |
362 | } |
363 | |
364 | return widgetList; |
365 | } |
366 | |
367 | void PluginDelegate::updateItemWidgets(const QList<QWidget *> &widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const |
368 | { |
369 | int = 0; |
370 | QPushButton * = nullptr; |
371 | if (widgets.count() == 4) { |
372 | extraButton = static_cast<QPushButton *>(widgets[3]); |
373 | extraButtonWidth = extraButton->sizeHint().width() + s_margin; |
374 | } |
375 | auto checkBox = static_cast<QCheckBox *>(widgets[0]); |
376 | checkBox->resize(checkBox->sizeHint()); |
377 | checkBox->move(ax: pluginSelector_d->dependantLayoutValue(value: s_margin, width: checkBox->sizeHint().width(), totalWidth: option.rect.width()), |
378 | ay: option.rect.height() / 2 - checkBox->sizeHint().height() / 2); |
379 | |
380 | auto aboutPushButton = static_cast<QPushButton *>(widgets[1]); |
381 | const QSize aboutPushButtonSizeHint = aboutPushButton->sizeHint(); |
382 | aboutPushButton->resize(aboutPushButtonSizeHint); |
383 | aboutPushButton->move(ax: pluginSelector_d->dependantLayoutValue(value: option.rect.width() - s_margin - aboutPushButtonSizeHint.width() - extraButtonWidth, |
384 | width: aboutPushButtonSizeHint.width(), |
385 | totalWidth: option.rect.width()), |
386 | ay: option.rect.height() / 2 - aboutPushButtonSizeHint.height() / 2); |
387 | |
388 | auto configurePushButton = static_cast<QPushButton *>(widgets[2]); |
389 | const QSize configurePushButtonSizeHint = configurePushButton->sizeHint(); |
390 | configurePushButton->resize(configurePushButtonSizeHint); |
391 | configurePushButton->move(ax: pluginSelector_d->dependantLayoutValue(value: option.rect.width() - s_margin * 2 - configurePushButtonSizeHint.width() |
392 | - aboutPushButtonSizeHint.width() - extraButtonWidth, |
393 | width: configurePushButtonSizeHint.width(), |
394 | totalWidth: option.rect.width()), |
395 | ay: option.rect.height() / 2 - configurePushButtonSizeHint.height() / 2); |
396 | |
397 | if (extraButton) { |
398 | const QSize = extraButton->sizeHint(); |
399 | extraButton->resize(extraPushButtonSizeHint); |
400 | extraButton->move(ax: pluginSelector_d->dependantLayoutValue(value: option.rect.width() - extraButtonWidth, width: extraPushButtonSizeHint.width(), totalWidth: option.rect.width()), |
401 | ay: option.rect.height() / 2 - extraPushButtonSizeHint.height() / 2); |
402 | } |
403 | |
404 | if (!index.isValid() || !index.internalPointer()) { |
405 | checkBox->setVisible(false); |
406 | aboutPushButton->setVisible(false); |
407 | configurePushButton->setVisible(false); |
408 | if (extraButton) { |
409 | extraButton->setVisible(false); |
410 | } |
411 | } else { |
412 | const bool enabledByDefault = index.model()->data(index, role: KPluginModel::EnabledByDefaultRole).toBool(); |
413 | const bool enabled = index.model()->data(index, role: KPluginModel::EnabledRole).toBool(); |
414 | checkBox->setProperty(name: "_kde_highlight_neutral" , value: pluginSelector_d->showDefaultIndicator && enabledByDefault != enabled); |
415 | checkBox->setChecked(index.model()->data(index, role: Qt::CheckStateRole).toBool()); |
416 | checkBox->setEnabled(index.model()->data(index, role: KPluginModel::IsChangeableRole).toBool()); |
417 | configurePushButton->setVisible(index.model()->data(index, role: KPluginModel::ConfigRole).value<KPluginMetaData>().isValid()); |
418 | configurePushButton->setEnabled(index.model()->data(index, role: Qt::CheckStateRole).toBool()); |
419 | } |
420 | } |
421 | |
422 | void PluginDelegate::slotStateChanged(bool state) |
423 | { |
424 | if (!focusedIndex().isValid()) { |
425 | return; |
426 | } |
427 | |
428 | QModelIndex index = focusedIndex(); |
429 | |
430 | const_cast<QAbstractItemModel *>(index.model())->setData(index, value: state, role: Qt::CheckStateRole); |
431 | } |
432 | |
433 | void PluginDelegate::slotAboutClicked() |
434 | { |
435 | const QModelIndex index = focusedIndex(); |
436 | |
437 | auto pluginMetaData = index.data(arole: KPluginModel::MetaDataRole).value<KPluginMetaData>(); |
438 | |
439 | auto *aboutPlugin = new KAboutPluginDialog(pluginMetaData, itemView()); |
440 | aboutPlugin->setAttribute(Qt::WA_DeleteOnClose); |
441 | aboutPlugin->show(); |
442 | } |
443 | |
444 | void PluginDelegate::slotConfigureClicked() |
445 | { |
446 | configure(idx: focusedIndex()); |
447 | } |
448 | |
449 | void PluginDelegate::configure(const QModelIndex &index) |
450 | { |
451 | const QAbstractItemModel *model = index.model(); |
452 | const auto kcm = model->data(index, role: KPluginModel::ConfigRole).value<KPluginMetaData>(); |
453 | |
454 | auto configDialog = new QDialog(itemView()); |
455 | configDialog->setAttribute(Qt::WA_DeleteOnClose); |
456 | configDialog->setModal(true); |
457 | configDialog->setWindowTitle(model->data(index, role: KPluginModel::NameRole).toString()); |
458 | |
459 | QWidget *kcmWrapper = new QWidget; |
460 | auto kcmInstance = KCModuleLoader::loadModule(metaData: kcm, parent: kcmWrapper, args: pluginSelector_d->kcmArguments); |
461 | |
462 | auto layout = new QVBoxLayout(configDialog); |
463 | layout->addWidget(kcmWrapper); |
464 | |
465 | auto buttonBox = new QDialogButtonBox(configDialog); |
466 | buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults); |
467 | KGuiItem::assign(button: buttonBox->button(which: QDialogButtonBox::Ok), item: KStandardGuiItem::ok()); |
468 | KGuiItem::assign(button: buttonBox->button(which: QDialogButtonBox::Cancel), item: KStandardGuiItem::cancel()); |
469 | KGuiItem::assign(button: buttonBox->button(which: QDialogButtonBox::RestoreDefaults), item: KStandardGuiItem::defaults()); |
470 | connect(sender: buttonBox, signal: &QDialogButtonBox::accepted, context: configDialog, slot: &QDialog::accept); |
471 | connect(sender: buttonBox, signal: &QDialogButtonBox::rejected, context: configDialog, slot: &QDialog::reject); |
472 | connect(sender: configDialog, signal: &QDialog::accepted, context: this, slot: [kcmInstance, this, model, index]() { |
473 | Q_EMIT configCommitted(pluginId: model->data(index, role: KPluginModel::IdRole).toString()); |
474 | kcmInstance->save(); |
475 | }); |
476 | connect(sender: configDialog, signal: &QDialog::rejected, context: this, slot: [kcmInstance]() { |
477 | kcmInstance->load(); |
478 | }); |
479 | |
480 | connect(sender: buttonBox->button(which: QDialogButtonBox::RestoreDefaults), signal: &QAbstractButton::clicked, context: this, slot: [kcmInstance] { |
481 | kcmInstance->defaults(); |
482 | }); |
483 | layout->addWidget(buttonBox); |
484 | |
485 | // Load KCM right before showing it |
486 | kcmInstance->load(); |
487 | configDialog->show(); |
488 | } |
489 | |
490 | QFont PluginDelegate::titleFont(const QFont &baseFont) const |
491 | { |
492 | QFont retFont(baseFont); |
493 | retFont.setBold(true); |
494 | |
495 | return retFont; |
496 | } |
497 | |
498 | #include "moc_kpluginwidget.cpp" |
499 | #include "moc_kpluginwidget_p.cpp" |
500 | |