KDEGames

kgamethemeselector.cpp
1/*
2 SPDX-FileCopyrightText: 2009-2012 Stefan Majewsky <majewsky@gmx.net>
3
4 SPDX-License-Identifier: LGPL-2.0-only
5*/
6
7#include "kgamethemeselector.h"
8#include "kgamethemeselector_p.h"
9
10// KF
11#include <KLocalizedString>
12#include <KNSWidgets/Button>
13// Qt
14#include <QAbstractItemView>
15#include <QApplication>
16#include <QCloseEvent>
17#include <QDialog>
18#include <QDialogButtonBox>
19#include <QFont>
20#include <QFontMetrics>
21#include <QIcon>
22#include <QListWidget>
23#include <QPainter>
24#include <QPushButton>
25#include <QScreen>
26#include <QScrollBar>
27#include <QVBoxLayout>
28
29namespace Metrics
30{
31const int Padding = 6;
32const QSize ThumbnailBaseSize(64, 64);
33}
34
35// BEGIN KGameThemeSelector
36
37class KGameThemeSelectorPrivate
38{
39public:
40 KGameThemeSelector *const q;
41
42 KGameThemeProvider *m_provider;
44 QListWidget *m_list;
45 KNSWidgets::Button *m_knsButton = nullptr;
46 QString m_configFileName;
47
48public:
49 KGameThemeSelectorPrivate(KGameThemeProvider *provider, KGameThemeSelector::Options options, KGameThemeSelector *q)
50 : q(q)
51 , m_provider(provider)
52 , m_options(options)
53 {
54 }
55
56 void fillList();
57
58 void _k_updateListSelection(const KGameTheme *theme);
59 void _k_updateProviderSelection();
60};
61
62KGameThemeSelector::KGameThemeSelector(KGameThemeProvider *provider, Options options, QWidget *parent)
63 : QWidget(parent)
64 , d_ptr(new KGameThemeSelectorPrivate(provider, options, this))
65{
67
68 d->m_list = new QListWidget(this);
69 d->m_list->setSelectionMode(QAbstractItemView::SingleSelection);
70 d->m_list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
71 // load themes from provider
72 d->fillList();
73
74 // setup appearance of the theme list
75 KGameThemeDelegate *delegate = new KGameThemeDelegate(d->m_list);
76 QScreen *screen = QWidget::screen();
77 QSize screenSize = screen->availableSize();
78 if (screenSize.width() < 650 || screenSize.height() < 650) {
79 d->m_list->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
80 if (parent) {
81 d->m_list->setMinimumSize(0, 0);
82 } else {
83 // greater than zero to prevent zero sized dialog in some games
84 d->m_list->setMinimumSize(330, 200);
85 }
86 } else {
87 const QSize itemSizeHint = delegate->sizeHint(QStyleOptionViewItem(), QModelIndex());
88 const QSize scrollBarSizeHint = d->m_list->verticalScrollBar()->sizeHint();
89 d->m_list->setMinimumSize(itemSizeHint.width() + 2 * scrollBarSizeHint.width(), 4.1 * itemSizeHint.height());
90 }
91
92 // monitor change selection in both directions
93 connect(d->m_provider, &KGameThemeProvider::currentThemeChanged, this, [this](const KGameTheme *theme) {
94 Q_D(KGameThemeSelector);
95 d->_k_updateListSelection(theme);
96 });
97 connect(d->m_list, &QListWidget::itemSelectionChanged, this, [this]() {
98 Q_D(KGameThemeSelector);
99 d->_k_updateProviderSelection();
100 });
101 // setup main layout
102 QVBoxLayout *layout = new QVBoxLayout(this);
103 layout->setContentsMargins(0, 0, 0, 0);
104 layout->addWidget(d->m_list);
105 // setup KNS button
106 if (options & EnableNewStuffDownload) {
107 QString name = QCoreApplication::applicationName() + QStringLiteral(".knsrc");
108 d->m_knsButton = new KNSWidgets::Button(i18nc("@action:button", "Download New Themes…"), name, this);
109 QHBoxLayout *hLayout = new QHBoxLayout();
110 hLayout->addStretch(1);
111 hLayout->addWidget(d->m_knsButton);
112 layout->addLayout(hLayout);
113 connect(d->m_knsButton, &KNSWidgets::Button::dialogFinished, this, [this](const QList<KNSCore::Entry> &changedEntries) {
114 Q_D(KGameThemeSelector);
115 if (!changedEntries.isEmpty()) {
116 d->m_provider->rediscoverThemes();
117 d->fillList();
118 }
119 // restore previous selection
120 d->_k_updateListSelection(d->m_provider->currentTheme());
121 });
122 }
123}
124
125void KGameThemeSelector::setNewStuffConfigFileName(const QString &configName)
126{
128 d->m_configFileName = configName;
129}
130
131KGameThemeSelector::~KGameThemeSelector() = default;
132
133void KGameThemeSelectorPrivate::fillList()
134{
135 m_list->clear();
136 const auto themes = m_provider->themes();
137 for (const KGameTheme *theme : themes) {
138 QListWidgetItem *item = new QListWidgetItem(theme->name(), m_list);
139 item->setData(Qt::DecorationRole, m_provider->generatePreview(theme, Metrics::ThumbnailBaseSize));
140 item->setData(KGameThemeDelegate::DescriptionRole, theme->description());
141 item->setData(KGameThemeDelegate::AuthorRole, theme->author());
142 item->setData(KGameThemeDelegate::AuthorEmailRole, theme->authorEmail());
143 item->setData(KGameThemeDelegate::IdRole, theme->identifier());
144 }
145 _k_updateListSelection(m_provider->currentTheme());
146}
147
148void KGameThemeSelectorPrivate::_k_updateListSelection(const KGameTheme *theme)
149{
150 for (int idx = 0; idx < m_list->count(); ++idx) {
151 QListWidgetItem *item = m_list->item(idx);
152 const QByteArray thisId = item->data(KGameThemeDelegate::IdRole).toByteArray();
153 if (thisId == theme->identifier()) {
155 return;
156 }
157 }
158 // make sure that something is selected
159 if (m_list->count() > 0) {
161 }
162}
163
164void KGameThemeSelectorPrivate::_k_updateProviderSelection()
165{
166 const QListWidgetItem *selItem = m_list->selectedItems().value(0);
167 if (!selItem) {
168 return;
169 }
170 const QByteArray selId = selItem->data(KGameThemeDelegate::IdRole).toByteArray();
171 // select the theme with this identifier
172 const auto themes = m_provider->themes();
173 for (const KGameTheme *theme : themes) {
174 if (theme->identifier() == selId) {
175 m_provider->setCurrentTheme(theme);
176 }
177 }
178}
179
180class Q_DECL_HIDDEN KGameThemeSelector::Dialog : public QDialog
181{
182public:
183 Dialog(KGameThemeSelector *sel, const QString &caption)
184 : mSelector(sel)
185 {
186 QVBoxLayout *mainLayout = new QVBoxLayout;
187 setLayout(mainLayout);
188 mainLayout->addWidget(sel);
189
190 // replace
191 QPushButton *btn = sel->d_ptr->m_knsButton;
192
193 QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
194
195 if (btn) {
196 btn->hide();
197
198 QPushButton *stuff = new QPushButton(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")), btn->text());
199 buttonBox->addButton(stuff, QDialogButtonBox::ActionRole);
201
204 } else {
207 }
208 // window caption
209 if (caption.isEmpty()) {
210 setWindowTitle(i18nc("@title:window config dialog", "Select theme"));
211 } else {
212 setWindowTitle(caption);
213 }
214
215 mainLayout->addWidget(buttonBox);
216 show();
217 }
218
219protected:
220 void closeEvent(QCloseEvent *event) override
221 {
222 event->accept();
223 // delete myself, but *not* the KGameThemeSelector
224 mSelector->setParent(nullptr);
225 deleteLater();
226 // restore the KNS button
227 if (mSelector->d_ptr->m_knsButton) {
228 mSelector->d_ptr->m_knsButton->show();
229 }
230 }
231
232private:
233 KGameThemeSelector *mSelector;
234};
235
237{
238 if (!isVisible()) {
239 new KGameThemeSelector::Dialog(this, caption);
240 }
241}
242
243// END KGameThemeSelector
244// BEGIN KGameThemeDelegate
245
246KGameThemeDelegate::KGameThemeDelegate(QObject *parent)
247 : QStyledItemDelegate(parent)
248{
250 if (view)
251 view->setItemDelegate(this);
252}
253
254QRect KGameThemeDelegate::thumbnailRect(QRect baseRect) const
255{
256 QRect thumbnailBaseRect(QPoint(Metrics::Padding + baseRect.left(), 0), Metrics::ThumbnailBaseSize);
257 thumbnailBaseRect.moveCenter(QPoint(thumbnailBaseRect.center().x(), baseRect.center().y()));
259 thumbnailBaseRect.moveRight(baseRect.right() - Metrics::Padding);
260 return thumbnailBaseRect;
261}
262
263void KGameThemeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
264{
265 const bool rtl = option.direction == Qt::RightToLeft;
266 QRect baseRect = option.rect;
267 // draw background
269 // draw thumbnail
270 QRect thumbnailBaseRect = this->thumbnailRect(baseRect);
271 const QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
272 QApplication::style()->drawItemPixmap(painter, thumbnailBaseRect, Qt::AlignCenter, thumbnail);
273
274 // find metrics: text
275 QStringList texts;
276 QList<QFont> fonts;
277 texts.reserve(3);
278 fonts.reserve(3);
279 {
281 if (name.isEmpty())
282 name = i18nc("@item name of theme, missing", "[No name]");
283 texts << name;
284 QFont theFont(painter->font());
285 theFont.setBold(true);
286 fonts << theFont;
287 }
288 {
289 QString comment = index.data(DescriptionRole).toString();
290 if (!comment.isEmpty()) {
291 texts << comment;
292 fonts << painter->font();
293 }
294 }
295 {
296 QString author = index.data(AuthorRole).toString();
297 if (!author.isEmpty()) {
298 const QString authorString = i18nc("@info author attribution", "By %1", author);
299 texts << authorString;
300 QFont theFont(painter->font());
301 theFont.setItalic(true);
302 fonts << theFont;
303 }
304 }
305 // TODO: display AuthorEmailRole
306 QList<QRect> textRects;
307 textRects.reserve(texts.size());
308 int totalTextHeight = 0;
309 for (int i = 0; i < texts.count(); ++i) {
310 QFontMetrics fm(fonts[i]);
311 textRects << fm.boundingRect(texts[i]);
312 textRects[i].setHeight(qMax(textRects[i].height(), fm.lineSpacing()));
313 totalTextHeight += textRects[i].height();
314 }
315 QRect textBaseRect(baseRect);
316 if (rtl) {
317 textBaseRect.setRight(thumbnailBaseRect.left() - Metrics::Padding);
318 textBaseRect.adjust(Metrics::Padding, Metrics::Padding, 0, -Metrics::Padding);
319 } else {
320 textBaseRect.setLeft(thumbnailBaseRect.right() + Metrics::Padding);
321 textBaseRect.adjust(0, Metrics::Padding, -Metrics::Padding, -Metrics::Padding);
322 }
323 textBaseRect.setHeight(totalTextHeight);
324 textBaseRect.moveTop(baseRect.top() + (baseRect.height() - textBaseRect.height()) / 2);
325 // draw texts
326 QRect currentTextRect(textBaseRect);
327 painter->save();
328 for (int i = 0; i < texts.count(); ++i) {
329 painter->setFont(fonts[i]);
330 const QRect &textRect = textRects[i];
331 currentTextRect.setHeight(textRect.height());
332 const QFontMetrics fm(fonts[i]);
333 const QString text = fm.elidedText(texts[i], Qt::ElideRight, currentTextRect.width());
334 painter->drawText(currentTextRect, Qt::AlignLeft | Qt::AlignVCenter, text);
335 currentTextRect.moveTop(currentTextRect.bottom());
336 }
337 painter->restore();
338}
339
340QSize KGameThemeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
341{
342 Q_UNUSED(option)
343 Q_UNUSED(index)
344 // TODO: take text size into account
345 return QSize(400, Metrics::ThumbnailBaseSize.height() + 2 * Metrics::Padding);
346}
347
348// END KGameThemeDelegate
349
350#include "moc_kgamethemeselector.cpp"
A theme provider manages KGameTheme instances, and maintains a selection of the currentTheme().
QList< const KGameTheme * > themes() const
void currentThemeChanged(const KGameTheme *theme)
Emitted when the current theme changes.
virtual QPixmap generatePreview(const KGameTheme *theme, QSize size)
Generate a preview pixmap for the given theme.
void setCurrentTheme(const KGameTheme *theme)
Select a new theme.
Theme selection widget.
void showAsDialog(const QString &caption=QString())
Create and show a non-modal dialog which displays this selector.
A theme describes the visual appearance of a game.
Definition kgametheme.h:60
void dialogFinished(const QList< KNSCore::Entry > &changedEntries)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString name(StandardAction id)
void clicked(bool checked)
void setItemDelegate(QAbstractItemDelegate *delegate)
QStyle * style()
void addLayout(QLayout *layout, int stretch)
void addStretch(int stretch)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
virtual void closeEvent(QCloseEvent *e) override
virtual void reject()
QPushButton * addButton(StandardButton button)
void setStandardButtons(StandardButtons buttons)
bool isRightToLeft()
QIcon fromTheme(const QString &name)
void setContentsMargins(const QMargins &margins)
qsizetype count() const const
void reserve(qsizetype size)
qsizetype size() const const
void clear()
void setCurrentRow(int row)
QListWidgetItem * item(int row) const const
void itemSelectionChanged()
QList< QListWidgetItem * > selectedItems() const const
void setCurrentItem(QListWidgetItem *item)
virtual QVariant data(int role) const const
virtual void setData(int role, const QVariant &value)
QVariant data(int role) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QObject * parent() const const
T qobject_cast(QObject *object)
void drawText(const QPoint &position, const QString &text)
const QFont & font() const const
void restore()
void save()
void setFont(const QFont &font)
int y() const const
QPoint center() const const
int height() const const
int left() const const
int right() const const
int top() const const
int height() const const
int width() const const
bool isEmpty() const const
PE_PanelItemViewItem
virtual void drawItemPixmap(QPainter *painter, const QRect &rectangle, int alignment, const QPixmap &pixmap) const const
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
AlignCenter
DecorationRole
RightToLeft
ElideRight
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QByteArray toByteArray() const const
QString toString() const const
T value() const const
void hide()
QScreen * screen() const const
void setLayout(QLayout *layout)
void show()
bool isVisible() const const
void setWindowTitle(const QString &)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 24 2024 11:48:56 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.