aboutsummaryrefslogtreecommitdiff
path: root/launcher/ui/widgets
diff options
context:
space:
mode:
authorflow <flowlnlnln@gmail.com>2022-09-07 08:27:11 -0300
committerGitHub <noreply@github.com>2022-09-07 08:27:11 -0300
commit1b0ca476824ad3d704de70720184d2f1e194d2f5 (patch)
tree6d5574a10b1fd34af511f9126c51e62b5a994516 /launcher/ui/widgets
parent8e3f5c33057175b83ef276914d81ed4d5616d6e5 (diff)
parent4a8abc948ef15664dbde196ba77daccdf3d623ad (diff)
downloadPrismLauncher-1b0ca476824ad3d704de70720184d2f1e194d2f5.tar.gz
PrismLauncher-1b0ca476824ad3d704de70720184d2f1e194d2f5.tar.bz2
PrismLauncher-1b0ca476824ad3d704de70720184d2f1e194d2f5.zip
Merge pull request #939 from flowln/mod_downloader_improve
Some more UI / UX improvements to the mod downloader!
Diffstat (limited to 'launcher/ui/widgets')
-rw-r--r--launcher/ui/widgets/Common.cpp22
-rw-r--r--launcher/ui/widgets/Common.h9
-rw-r--r--launcher/ui/widgets/PageContainer.cpp9
-rw-r--r--launcher/ui/widgets/PageContainer.h4
-rw-r--r--launcher/ui/widgets/ProgressWidget.cpp94
-rw-r--r--launcher/ui/widgets/ProgressWidget.h48
-rw-r--r--launcher/ui/widgets/ProjectItem.cpp78
-rw-r--r--launcher/ui/widgets/ProjectItem.h25
8 files changed, 237 insertions, 52 deletions
diff --git a/launcher/ui/widgets/Common.cpp b/launcher/ui/widgets/Common.cpp
index f72f3596..097bb6d4 100644
--- a/launcher/ui/widgets/Common.cpp
+++ b/launcher/ui/widgets/Common.cpp
@@ -1,27 +1,33 @@
#include "Common.h"
// Origin: Qt
-QStringList viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
- qreal &widthUsed)
+// More specifically, this is a trimmed down version on the algorithm in:
+// https://code.woboq.org/qt5/qtbase/src/widgets/styles/qcommonstyle.cpp.html#846
+QList<std::pair<qreal, QString>> viewItemTextLayout(QTextLayout& textLayout, int lineWidth, qreal& height)
{
- QStringList lines;
+ QList<std::pair<qreal, QString>> lines;
height = 0;
- widthUsed = 0;
+
textLayout.beginLayout();
+
QString str = textLayout.text();
- while (true)
- {
+ while (true) {
QTextLine line = textLayout.createLine();
+
if (!line.isValid())
break;
if (line.textLength() == 0)
break;
+
line.setLineWidth(lineWidth);
line.setPosition(QPointF(0, height));
+
height += line.height();
- lines.append(str.mid(line.textStart(), line.textLength()));
- widthUsed = qMax(widthUsed, line.naturalTextWidth());
+
+ lines.append(std::make_pair(line.naturalTextWidth(), str.mid(line.textStart(), line.textLength())));
}
+
textLayout.endLayout();
+
return lines;
}
diff --git a/launcher/ui/widgets/Common.h b/launcher/ui/widgets/Common.h
index b3fbe1a0..b3dd5ca8 100644
--- a/launcher/ui/widgets/Common.h
+++ b/launcher/ui/widgets/Common.h
@@ -1,6 +1,9 @@
#pragma once
-#include <QStringList>
+
#include <QTextLayout>
-QStringList viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
- qreal &widthUsed); \ No newline at end of file
+/** Cuts out the text in textLayout into smaller pieces, according to the lineWidth.
+ * Returns a list of pairs, each containing the width of that line and that line's string, respectively.
+ * The total height of those lines is set in the last argument, 'height'.
+ */
+QList<std::pair<qreal, QString>> viewItemTextLayout(QTextLayout& textLayout, int lineWidth, qreal& height);
diff --git a/launcher/ui/widgets/PageContainer.cpp b/launcher/ui/widgets/PageContainer.cpp
index 419ccb66..8d606820 100644
--- a/launcher/ui/widgets/PageContainer.cpp
+++ b/launcher/ui/widgets/PageContainer.cpp
@@ -244,7 +244,14 @@ void PageContainer::help()
void PageContainer::currentChanged(const QModelIndex &current)
{
- showPage(current.isValid() ? m_proxyModel->mapToSource(current).row() : -1);
+ int selected_index = current.isValid() ? m_proxyModel->mapToSource(current).row() : -1;
+
+ auto* selected = m_model->pages().at(selected_index);
+ auto* previous = m_currentPage;
+
+ emit selectedPageChanged(previous, selected);
+
+ showPage(selected_index);
}
bool PageContainer::prepareToClose()
diff --git a/launcher/ui/widgets/PageContainer.h b/launcher/ui/widgets/PageContainer.h
index 86f549eb..80d87a9b 100644
--- a/launcher/ui/widgets/PageContainer.h
+++ b/launcher/ui/widgets/PageContainer.h
@@ -95,6 +95,10 @@ private:
public slots:
void help();
+signals:
+ /** Emitted when the currently selected page is changed */
+ void selectedPageChanged(BasePage* previous, BasePage* selected);
+
private slots:
void currentChanged(const QModelIndex &current);
void showPage(int row);
diff --git a/launcher/ui/widgets/ProgressWidget.cpp b/launcher/ui/widgets/ProgressWidget.cpp
index 911e555d..b60d9a7a 100644
--- a/launcher/ui/widgets/ProgressWidget.cpp
+++ b/launcher/ui/widgets/ProgressWidget.cpp
@@ -1,66 +1,104 @@
// Licensed under the Apache-2.0 license. See README.md for details.
#include "ProgressWidget.h"
-#include <QProgressBar>
+#include <QEventLoop>
#include <QLabel>
+#include <QProgressBar>
#include <QVBoxLayout>
-#include <QEventLoop>
#include "tasks/Task.h"
-ProgressWidget::ProgressWidget(QWidget *parent)
- : QWidget(parent)
+ProgressWidget::ProgressWidget(QWidget* parent, bool show_label) : QWidget(parent)
{
- m_label = new QLabel(this);
- m_label->setWordWrap(true);
+ auto* layout = new QVBoxLayout(this);
+
+ if (show_label) {
+ m_label = new QLabel(this);
+ m_label->setWordWrap(true);
+ layout->addWidget(m_label);
+ }
+
m_bar = new QProgressBar(this);
m_bar->setMinimum(0);
m_bar->setMaximum(100);
- QVBoxLayout *layout = new QVBoxLayout(this);
- layout->addWidget(m_label);
layout->addWidget(m_bar);
- layout->addStretch();
+
setLayout(layout);
}
-void ProgressWidget::start(std::shared_ptr<Task> task)
+void ProgressWidget::reset()
+{
+ m_bar->reset();
+}
+
+void ProgressWidget::progressFormat(QString format)
+{
+ if (format.isEmpty())
+ m_bar->setTextVisible(false);
+ else
+ m_bar->setFormat(format);
+}
+
+void ProgressWidget::watch(Task* task)
{
+ if (!task)
+ return;
+
if (m_task)
- {
- disconnect(m_task.get(), 0, this, 0);
- }
+ disconnect(m_task, nullptr, this, nullptr);
+
m_task = task;
- connect(m_task.get(), &Task::finished, this, &ProgressWidget::handleTaskFinish);
- connect(m_task.get(), &Task::status, this, &ProgressWidget::handleTaskStatus);
- connect(m_task.get(), &Task::progress, this, &ProgressWidget::handleTaskProgress);
- connect(m_task.get(), &Task::destroyed, this, &ProgressWidget::taskDestroyed);
+
+ connect(m_task, &Task::finished, this, &ProgressWidget::handleTaskFinish);
+ connect(m_task, &Task::status, this, &ProgressWidget::handleTaskStatus);
+ connect(m_task, &Task::progress, this, &ProgressWidget::handleTaskProgress);
+ connect(m_task, &Task::destroyed, this, &ProgressWidget::taskDestroyed);
+
+ show();
+}
+
+void ProgressWidget::start(Task* task)
+{
+ watch(task);
if (!m_task->isRunning())
- {
- QMetaObject::invokeMethod(m_task.get(), "start", Qt::QueuedConnection);
- }
+ QMetaObject::invokeMethod(m_task, "start", Qt::QueuedConnection);
}
+
bool ProgressWidget::exec(std::shared_ptr<Task> task)
{
QEventLoop loop;
+
connect(task.get(), &Task::finished, &loop, &QEventLoop::quit);
- start(task);
+
+ start(task.get());
+
if (task->isRunning())
- {
loop.exec();
- }
+
return task->wasSuccessful();
}
+void ProgressWidget::show()
+{
+ setHidden(false);
+}
+void ProgressWidget::hide()
+{
+ setHidden(true);
+}
+
void ProgressWidget::handleTaskFinish()
{
- if (!m_task->wasSuccessful())
- {
+ if (!m_task->wasSuccessful() && m_label)
m_label->setText(m_task->failReason());
- }
+
+ if (m_hide_if_inactive)
+ hide();
}
-void ProgressWidget::handleTaskStatus(const QString &status)
+void ProgressWidget::handleTaskStatus(const QString& status)
{
- m_label->setText(status);
+ if (m_label)
+ m_label->setText(status);
}
void ProgressWidget::handleTaskProgress(qint64 current, qint64 total)
{
diff --git a/launcher/ui/widgets/ProgressWidget.h b/launcher/ui/widgets/ProgressWidget.h
index fa67748a..4d9097b8 100644
--- a/launcher/ui/widgets/ProgressWidget.h
+++ b/launcher/ui/widgets/ProgressWidget.h
@@ -9,24 +9,48 @@ class Task;
class QProgressBar;
class QLabel;
-class ProgressWidget : public QWidget
-{
+class ProgressWidget : public QWidget {
Q_OBJECT
-public:
- explicit ProgressWidget(QWidget *parent = nullptr);
+ public:
+ explicit ProgressWidget(QWidget* parent = nullptr, bool show_label = true);
-public slots:
- void start(std::shared_ptr<Task> task);
+ /** Whether to hide the widget automatically if it's watching no running task. */
+ void hideIfInactive(bool hide) { m_hide_if_inactive = hide; }
+
+ /** Reset the displayed progress to 0 */
+ void reset();
+
+ /** The text that shows up in the middle of the progress bar.
+ * By default it's '%p%', with '%p' being the total progress in percentage.
+ */
+ void progressFormat(QString);
+
+ public slots:
+ /** Watch the progress of a task. */
+ void watch(Task* task);
+
+ /** Watch the progress of a task, and start it if needed */
+ void start(Task* task);
+
+ /** Blocking way of waiting for a task to finish. */
bool exec(std::shared_ptr<Task> task);
-private slots:
+ /** Un-hide the widget if needed. */
+ void show();
+
+ /** Make the widget invisible. */
+ void hide();
+
+ private slots:
void handleTaskFinish();
- void handleTaskStatus(const QString &status);
+ void handleTaskStatus(const QString& status);
void handleTaskProgress(qint64 current, qint64 total);
void taskDestroyed();
-private:
- QLabel *m_label;
- QProgressBar *m_bar;
- std::shared_ptr<Task> m_task;
+ private:
+ QLabel* m_label = nullptr;
+ QProgressBar* m_bar = nullptr;
+ Task* m_task = nullptr;
+
+ bool m_hide_if_inactive = false;
};
diff --git a/launcher/ui/widgets/ProjectItem.cpp b/launcher/ui/widgets/ProjectItem.cpp
new file mode 100644
index 00000000..56ae35fb
--- /dev/null
+++ b/launcher/ui/widgets/ProjectItem.cpp
@@ -0,0 +1,78 @@
+#include "ProjectItem.h"
+
+#include "Common.h"
+
+#include <QIcon>
+#include <QPainter>
+
+ProjectItemDelegate::ProjectItemDelegate(QWidget* parent) : QStyledItemDelegate(parent) {}
+
+void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
+{
+ painter->save();
+
+ QStyleOptionViewItem opt(option);
+ initStyleOption(&opt, index);
+
+ auto& rect = opt.rect;
+ auto icon_width = rect.height(), icon_height = rect.height();
+ auto remaining_width = rect.width() - icon_width;
+
+ if (opt.state & QStyle::State_Selected) {
+ painter->fillRect(rect, opt.palette.highlight());
+ painter->setPen(opt.palette.highlightedText().color());
+ } else if (opt.state & QStyle::State_MouseOver) {
+ painter->fillRect(rect, opt.palette.window());
+ }
+
+ { // Icon painting
+ // Square-sized, occupying the left portion
+ opt.icon.paint(painter, rect.x(), rect.y(), icon_width, icon_height);
+ }
+
+ { // Title painting
+ auto title = index.data(UserDataTypes::TITLE).toString();
+
+ painter->save();
+
+ auto font = opt.font;
+ if (index.data(UserDataTypes::SELECTED).toBool()) {
+ // Set nice font
+ font.setBold(true);
+ font.setUnderline(true);
+ }
+
+ font.setPointSize(font.pointSize() + 2);
+ painter->setFont(font);
+
+ // On the top, aligned to the left after the icon
+ painter->drawText(rect.x() + icon_width, rect.y() + QFontMetrics(font).height(), title);
+
+ painter->restore();
+ }
+
+ { // Description painting
+ auto description = index.data(UserDataTypes::DESCRIPTION).toString();
+
+ QTextLayout text_layout(description, opt.font);
+
+ qreal height = 0;
+ auto cut_text = viewItemTextLayout(text_layout, remaining_width, height);
+
+ // Get first line unconditionally
+ description = cut_text.first().second;
+ // Get second line, elided if needed
+ if (cut_text.size() > 1) {
+ if (cut_text.size() > 2)
+ description += opt.fontMetrics.elidedText(cut_text.at(1).second, opt.textElideMode, cut_text.at(1).first);
+ else
+ description += cut_text.at(1).second;
+ }
+
+ // On the bottom, aligned to the left after the icon, and featuring at most two lines of text (with some margin space to spare)
+ painter->drawText(rect.x() + icon_width, rect.y() + rect.height() - 2.2 * opt.fontMetrics.height(), remaining_width,
+ 2 * opt.fontMetrics.height(), Qt::TextWordWrap, description);
+ }
+
+ painter->restore();
+}
diff --git a/launcher/ui/widgets/ProjectItem.h b/launcher/ui/widgets/ProjectItem.h
new file mode 100644
index 00000000..f668edf6
--- /dev/null
+++ b/launcher/ui/widgets/ProjectItem.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <QStyledItemDelegate>
+
+/* Custom data types for our custom list models :) */
+enum UserDataTypes {
+ TITLE = 257, // QString
+ DESCRIPTION = 258, // QString
+ SELECTED = 259 // bool
+};
+
+/** This is an item delegate composed of:
+ * - An Icon on the left
+ * - A title
+ * - A description
+ * */
+class ProjectItemDelegate final : public QStyledItemDelegate {
+ Q_OBJECT
+
+ public:
+ ProjectItemDelegate(QWidget* parent);
+
+ void paint(QPainter*, const QStyleOptionViewItem&, const QModelIndex&) const override;
+
+};