NOISSUE Flatten gui and logic libraries into MultiMC
This commit is contained in:
27
launcher/widgets/Common.cpp
Normal file
27
launcher/widgets/Common.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "Common.h"
|
||||
|
||||
// Origin: Qt
|
||||
QStringList viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
|
||||
qreal &widthUsed)
|
||||
{
|
||||
QStringList lines;
|
||||
height = 0;
|
||||
widthUsed = 0;
|
||||
textLayout.beginLayout();
|
||||
QString str = textLayout.text();
|
||||
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());
|
||||
}
|
||||
textLayout.endLayout();
|
||||
return lines;
|
||||
}
|
6
launcher/widgets/Common.h
Normal file
6
launcher/widgets/Common.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <QStringList>
|
||||
#include <QTextLayout>
|
||||
|
||||
QStringList viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
|
||||
qreal &widthUsed);
|
49
launcher/widgets/CustomCommands.cpp
Normal file
49
launcher/widgets/CustomCommands.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include "CustomCommands.h"
|
||||
#include "ui_CustomCommands.h"
|
||||
|
||||
CustomCommands::~CustomCommands()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
CustomCommands::CustomCommands(QWidget* parent):
|
||||
QWidget(parent),
|
||||
ui(new Ui::CustomCommands)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
void CustomCommands::initialize(bool checkable, bool checked, const QString& prelaunch, const QString& wrapper, const QString& postexit)
|
||||
{
|
||||
ui->customCommandsGroupBox->setCheckable(checkable);
|
||||
if(checkable)
|
||||
{
|
||||
ui->customCommandsGroupBox->setChecked(checked);
|
||||
}
|
||||
ui->preLaunchCmdTextBox->setText(prelaunch);
|
||||
ui->wrapperCmdTextBox->setText(wrapper);
|
||||
ui->postExitCmdTextBox->setText(postexit);
|
||||
}
|
||||
|
||||
|
||||
bool CustomCommands::checked() const
|
||||
{
|
||||
if(!ui->customCommandsGroupBox->isCheckable())
|
||||
return true;
|
||||
return ui->customCommandsGroupBox->isChecked();
|
||||
}
|
||||
|
||||
QString CustomCommands::prelaunchCommand() const
|
||||
{
|
||||
return ui->preLaunchCmdTextBox->text();
|
||||
}
|
||||
|
||||
QString CustomCommands::wrapperCommand() const
|
||||
{
|
||||
return ui->wrapperCmdTextBox->text();
|
||||
}
|
||||
|
||||
QString CustomCommands::postexitCommand() const
|
||||
{
|
||||
return ui->postExitCmdTextBox->text();
|
||||
}
|
43
launcher/widgets/CustomCommands.h
Normal file
43
launcher/widgets/CustomCommands.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* Copyright 2018-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class CustomCommands;
|
||||
}
|
||||
|
||||
class CustomCommands : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CustomCommands(QWidget *parent = 0);
|
||||
virtual ~CustomCommands();
|
||||
void initialize(bool checkable, bool checked, const QString & prelaunch, const QString & wrapper, const QString & postexit);
|
||||
|
||||
bool checked() const;
|
||||
QString prelaunchCommand() const;
|
||||
QString wrapperCommand() const;
|
||||
QString postexitCommand() const;
|
||||
|
||||
private:
|
||||
Ui::CustomCommands *ui;
|
||||
};
|
||||
|
||||
|
107
launcher/widgets/CustomCommands.ui
Normal file
107
launcher/widgets/CustomCommands.ui
Normal file
@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CustomCommands</class>
|
||||
<widget class="QWidget" name="CustomCommands">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>518</width>
|
||||
<height>646</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="customCommandsGroupBox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Cus&tom Commands</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelPostExitCmd">
|
||||
<property name="text">
|
||||
<string>Post-exit command:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="preLaunchCmdTextBox"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelPreLaunchCmd">
|
||||
<property name="text">
|
||||
<string>Pre-launch command:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="postExitCmdTextBox"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelWrapperCmd">
|
||||
<property name="text">
|
||||
<string>Wrapper command:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="wrapperCmdTextBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelCustomCmdsDescription">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Pre-launch command runs before the instance launches and post-exit command runs after it exits.</p><p>Both will be run in MultiMC's working folder with extra environment variables:</p><ul><li>$INST_NAME - Name of the instance</li><li>$INST_ID - ID of the instance (its folder name)</li><li>$INST_DIR - absolute path of the instance</li><li>$INST_MC_DIR - absolute path of minecraft</li><li>$INST_JAVA - java binary used for launch</li><li>$INST_JAVA_ARGS - command-line parameters used for launch</li></ul><p>Wrapper command allows launching using an extra wrapper program (like 'optirun' on Linux)</p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
41
launcher/widgets/DropLabel.cpp
Normal file
41
launcher/widgets/DropLabel.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "DropLabel.h"
|
||||
|
||||
#include <QMimeData>
|
||||
#include <QDropEvent>
|
||||
|
||||
DropLabel::DropLabel(QWidget *parent) : QLabel(parent)
|
||||
{
|
||||
setAcceptDrops(true);
|
||||
}
|
||||
|
||||
void DropLabel::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
void DropLabel::dragMoveEvent(QDragMoveEvent *event)
|
||||
{
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
void DropLabel::dragLeaveEvent(QDragLeaveEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void DropLabel::dropEvent(QDropEvent *event)
|
||||
{
|
||||
const QMimeData *mimeData = event->mimeData();
|
||||
|
||||
if (!mimeData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mimeData->hasUrls()) {
|
||||
auto urls = mimeData->urls();
|
||||
emit droppedURLs(urls);
|
||||
}
|
||||
|
||||
event->acceptProposedAction();
|
||||
}
|
20
launcher/widgets/DropLabel.h
Normal file
20
launcher/widgets/DropLabel.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
class DropLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DropLabel(QWidget *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void droppedURLs(QList<QUrl> urls);
|
||||
|
||||
protected:
|
||||
void dropEvent(QDropEvent *event) override;
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
void dragMoveEvent(QDragMoveEvent *event) override;
|
||||
void dragLeaveEvent(QDragLeaveEvent *event) override;
|
||||
};
|
25
launcher/widgets/FocusLineEdit.cpp
Normal file
25
launcher/widgets/FocusLineEdit.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include "FocusLineEdit.h"
|
||||
#include <QDebug>
|
||||
|
||||
FocusLineEdit::FocusLineEdit(QWidget *parent) : QLineEdit(parent)
|
||||
{
|
||||
_selectOnMousePress = false;
|
||||
}
|
||||
|
||||
void FocusLineEdit::focusInEvent(QFocusEvent *e)
|
||||
{
|
||||
QLineEdit::focusInEvent(e);
|
||||
selectAll();
|
||||
_selectOnMousePress = true;
|
||||
}
|
||||
|
||||
void FocusLineEdit::mousePressEvent(QMouseEvent *me)
|
||||
{
|
||||
QLineEdit::mousePressEvent(me);
|
||||
if (_selectOnMousePress)
|
||||
{
|
||||
selectAll();
|
||||
_selectOnMousePress = false;
|
||||
}
|
||||
qDebug() << selectedText();
|
||||
}
|
17
launcher/widgets/FocusLineEdit.h
Normal file
17
launcher/widgets/FocusLineEdit.h
Normal file
@ -0,0 +1,17 @@
|
||||
#include <QLineEdit>
|
||||
|
||||
class FocusLineEdit : public QLineEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FocusLineEdit(QWidget *parent);
|
||||
virtual ~FocusLineEdit()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void focusInEvent(QFocusEvent *e);
|
||||
void mousePressEvent(QMouseEvent *me);
|
||||
|
||||
bool _selectOnMousePress;
|
||||
};
|
43
launcher/widgets/IconLabel.cpp
Normal file
43
launcher/widgets/IconLabel.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include "IconLabel.h"
|
||||
|
||||
#include <QStyle>
|
||||
#include <QStyleOption>
|
||||
#include <QLayout>
|
||||
#include <QPainter>
|
||||
#include <QRect>
|
||||
|
||||
IconLabel::IconLabel(QWidget *parent, QIcon icon, QSize size)
|
||||
: QWidget(parent), m_size(size), m_icon(icon)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
}
|
||||
|
||||
QSize IconLabel::sizeHint() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void IconLabel::setIcon(QIcon icon)
|
||||
{
|
||||
m_icon = icon;
|
||||
update();
|
||||
}
|
||||
|
||||
void IconLabel::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter p(this);
|
||||
QRect rect = contentsRect();
|
||||
int width = rect.width();
|
||||
int height = rect.height();
|
||||
if(width < height)
|
||||
{
|
||||
rect.setHeight(width);
|
||||
rect.translate(0, (height - width) / 2);
|
||||
}
|
||||
else if (width > height)
|
||||
{
|
||||
rect.setWidth(height);
|
||||
rect.translate((width - height) / 2, 0);
|
||||
}
|
||||
m_icon.paint(&p, rect);
|
||||
}
|
26
launcher/widgets/IconLabel.h
Normal file
26
launcher/widgets/IconLabel.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QIcon>
|
||||
|
||||
class QStyleOption;
|
||||
|
||||
/**
|
||||
* This is a trivial widget that paints a QIcon of the specified size.
|
||||
*/
|
||||
class IconLabel : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/// Create a line separator. orientation is the orientation of the line.
|
||||
explicit IconLabel(QWidget *parent, QIcon icon, QSize size);
|
||||
|
||||
virtual QSize sizeHint() const;
|
||||
virtual void paintEvent(QPaintEvent *);
|
||||
|
||||
void setIcon(QIcon icon);
|
||||
|
||||
private:
|
||||
QSize m_size;
|
||||
QIcon m_icon;
|
||||
};
|
58
launcher/widgets/InstanceCardWidget.ui
Normal file
58
launcher/widgets/InstanceCardWidget.ui
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>InstanceCardWidget</class>
|
||||
<widget class="QWidget" name="InstanceCardWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>473</width>
|
||||
<height>118</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QToolButton" name="iconButton">
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<property name="text">
|
||||
<string>&Name:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>instNameTextBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLineEdit" name="instNameTextBox"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="groupLabel">
|
||||
<property name="text">
|
||||
<string>&Group:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>groupBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QComboBox" name="groupBox">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
428
launcher/widgets/JavaSettingsWidget.cpp
Normal file
428
launcher/widgets/JavaSettingsWidget.cpp
Normal file
@ -0,0 +1,428 @@
|
||||
#include "JavaSettingsWidget.h"
|
||||
#include <MultiMC.h>
|
||||
|
||||
#include <java/JavaInstall.h>
|
||||
#include <dialogs/CustomMessageBox.h>
|
||||
#include <java/JavaUtils.h>
|
||||
#include <sys.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QSpinBox>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
#include <widgets/VersionSelectWidget.h>
|
||||
#include <FileSystem.h>
|
||||
#include <QFileDialog>
|
||||
|
||||
JavaSettingsWidget::JavaSettingsWidget(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
m_availableMemory = Sys::getSystemRam() / Sys::mebibyte;
|
||||
|
||||
goodIcon = MMC->getThemedIcon("status-good");
|
||||
yellowIcon = MMC->getThemedIcon("status-yellow");
|
||||
badIcon = MMC->getThemedIcon("status-bad");
|
||||
setupUi();
|
||||
|
||||
connect(m_minMemSpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryValueChanged(int)));
|
||||
connect(m_maxMemSpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryValueChanged(int)));
|
||||
connect(m_permGenSpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryValueChanged(int)));
|
||||
connect(m_versionWidget, &VersionSelectWidget::selectedVersionChanged, this, &JavaSettingsWidget::javaVersionSelected);
|
||||
connect(m_javaBrowseBtn, &QPushButton::clicked, this, &JavaSettingsWidget::on_javaBrowseBtn_clicked);
|
||||
connect(m_javaPathTextBox, &QLineEdit::textEdited, this, &JavaSettingsWidget::javaPathEdited);
|
||||
connect(m_javaStatusBtn, &QToolButton::clicked, this, &JavaSettingsWidget::on_javaStatusBtn_clicked);
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::setupUi()
|
||||
{
|
||||
setObjectName(QStringLiteral("javaSettingsWidget"));
|
||||
m_verticalLayout = new QVBoxLayout(this);
|
||||
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||
|
||||
m_versionWidget = new VersionSelectWidget(this);
|
||||
m_verticalLayout->addWidget(m_versionWidget);
|
||||
|
||||
m_horizontalLayout = new QHBoxLayout();
|
||||
m_horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
|
||||
m_javaPathTextBox = new QLineEdit(this);
|
||||
m_javaPathTextBox->setObjectName(QStringLiteral("javaPathTextBox"));
|
||||
|
||||
m_horizontalLayout->addWidget(m_javaPathTextBox);
|
||||
|
||||
m_javaBrowseBtn = new QPushButton(this);
|
||||
m_javaBrowseBtn->setObjectName(QStringLiteral("javaBrowseBtn"));
|
||||
|
||||
m_horizontalLayout->addWidget(m_javaBrowseBtn);
|
||||
|
||||
m_javaStatusBtn = new QToolButton(this);
|
||||
m_javaStatusBtn->setIcon(yellowIcon);
|
||||
m_horizontalLayout->addWidget(m_javaStatusBtn);
|
||||
|
||||
m_verticalLayout->addLayout(m_horizontalLayout);
|
||||
|
||||
m_memoryGroupBox = new QGroupBox(this);
|
||||
m_memoryGroupBox->setObjectName(QStringLiteral("memoryGroupBox"));
|
||||
m_gridLayout_2 = new QGridLayout(m_memoryGroupBox);
|
||||
m_gridLayout_2->setObjectName(QStringLiteral("gridLayout_2"));
|
||||
|
||||
m_labelMinMem = new QLabel(m_memoryGroupBox);
|
||||
m_labelMinMem->setObjectName(QStringLiteral("labelMinMem"));
|
||||
m_gridLayout_2->addWidget(m_labelMinMem, 0, 0, 1, 1);
|
||||
|
||||
m_minMemSpinBox = new QSpinBox(m_memoryGroupBox);
|
||||
m_minMemSpinBox->setObjectName(QStringLiteral("minMemSpinBox"));
|
||||
m_minMemSpinBox->setSuffix(QStringLiteral(" MB"));
|
||||
m_minMemSpinBox->setMinimum(128);
|
||||
m_minMemSpinBox->setMaximum(m_availableMemory);
|
||||
m_minMemSpinBox->setSingleStep(128);
|
||||
m_labelMinMem->setBuddy(m_minMemSpinBox);
|
||||
m_gridLayout_2->addWidget(m_minMemSpinBox, 0, 1, 1, 1);
|
||||
|
||||
m_labelMaxMem = new QLabel(m_memoryGroupBox);
|
||||
m_labelMaxMem->setObjectName(QStringLiteral("labelMaxMem"));
|
||||
m_gridLayout_2->addWidget(m_labelMaxMem, 1, 0, 1, 1);
|
||||
|
||||
m_maxMemSpinBox = new QSpinBox(m_memoryGroupBox);
|
||||
m_maxMemSpinBox->setObjectName(QStringLiteral("maxMemSpinBox"));
|
||||
m_maxMemSpinBox->setSuffix(QStringLiteral(" MB"));
|
||||
m_maxMemSpinBox->setMinimum(128);
|
||||
m_maxMemSpinBox->setMaximum(m_availableMemory);
|
||||
m_maxMemSpinBox->setSingleStep(128);
|
||||
m_labelMaxMem->setBuddy(m_maxMemSpinBox);
|
||||
m_gridLayout_2->addWidget(m_maxMemSpinBox, 1, 1, 1, 1);
|
||||
|
||||
m_labelPermGen = new QLabel(m_memoryGroupBox);
|
||||
m_labelPermGen->setObjectName(QStringLiteral("labelPermGen"));
|
||||
m_labelPermGen->setText(QStringLiteral("PermGen:"));
|
||||
m_gridLayout_2->addWidget(m_labelPermGen, 2, 0, 1, 1);
|
||||
m_labelPermGen->setVisible(false);
|
||||
|
||||
m_permGenSpinBox = new QSpinBox(m_memoryGroupBox);
|
||||
m_permGenSpinBox->setObjectName(QStringLiteral("permGenSpinBox"));
|
||||
m_permGenSpinBox->setSuffix(QStringLiteral(" MB"));
|
||||
m_permGenSpinBox->setMinimum(64);
|
||||
m_permGenSpinBox->setMaximum(m_availableMemory);
|
||||
m_permGenSpinBox->setSingleStep(8);
|
||||
m_gridLayout_2->addWidget(m_permGenSpinBox, 2, 1, 1, 1);
|
||||
m_permGenSpinBox->setVisible(false);
|
||||
|
||||
m_verticalLayout->addWidget(m_memoryGroupBox);
|
||||
|
||||
retranslate();
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::initialize()
|
||||
{
|
||||
m_versionWidget->initialize(MMC->javalist().get());
|
||||
m_versionWidget->setResizeOn(2);
|
||||
auto s = MMC->settings();
|
||||
// Memory
|
||||
observedMinMemory = s->get("MinMemAlloc").toInt();
|
||||
observedMaxMemory = s->get("MaxMemAlloc").toInt();
|
||||
observedPermGenMemory = s->get("PermGen").toInt();
|
||||
m_minMemSpinBox->setValue(observedMinMemory);
|
||||
m_maxMemSpinBox->setValue(observedMaxMemory);
|
||||
m_permGenSpinBox->setValue(observedPermGenMemory);
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::refresh()
|
||||
{
|
||||
m_versionWidget->loadList();
|
||||
}
|
||||
|
||||
JavaSettingsWidget::ValidationStatus JavaSettingsWidget::validate()
|
||||
{
|
||||
switch(javaStatus)
|
||||
{
|
||||
default:
|
||||
case JavaStatus::NotSet:
|
||||
case JavaStatus::DoesNotExist:
|
||||
case JavaStatus::DoesNotStart:
|
||||
case JavaStatus::ReturnedInvalidData:
|
||||
{
|
||||
int button = CustomMessageBox::selectable(
|
||||
this,
|
||||
tr("No Java version selected"),
|
||||
tr("You didn't select a Java version or selected something that doesn't work.\n"
|
||||
"MultiMC will not be able to start Minecraft.\n"
|
||||
"Do you wish to proceed without any Java?"
|
||||
"\n\n"
|
||||
"You can change the Java version in the settings later.\n"
|
||||
),
|
||||
QMessageBox::Warning,
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::NoButton
|
||||
)->exec();
|
||||
if(button == QMessageBox::No)
|
||||
{
|
||||
return ValidationStatus::Bad;
|
||||
}
|
||||
return ValidationStatus::JavaBad;
|
||||
}
|
||||
break;
|
||||
case JavaStatus::Pending:
|
||||
{
|
||||
return ValidationStatus::Bad;
|
||||
}
|
||||
case JavaStatus::Good:
|
||||
{
|
||||
return ValidationStatus::AllOK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString JavaSettingsWidget::javaPath() const
|
||||
{
|
||||
return m_javaPathTextBox->text();
|
||||
}
|
||||
|
||||
int JavaSettingsWidget::maxHeapSize() const
|
||||
{
|
||||
return m_maxMemSpinBox->value();
|
||||
}
|
||||
|
||||
int JavaSettingsWidget::minHeapSize() const
|
||||
{
|
||||
return m_minMemSpinBox->value();
|
||||
}
|
||||
|
||||
bool JavaSettingsWidget::permGenEnabled() const
|
||||
{
|
||||
return m_permGenSpinBox->isVisible();
|
||||
}
|
||||
|
||||
int JavaSettingsWidget::permGenSize() const
|
||||
{
|
||||
return m_permGenSpinBox->value();
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::memoryValueChanged(int)
|
||||
{
|
||||
bool actuallyChanged = false;
|
||||
int min = m_minMemSpinBox->value();
|
||||
int max = m_maxMemSpinBox->value();
|
||||
int permgen = m_permGenSpinBox->value();
|
||||
QObject *obj = sender();
|
||||
if (obj == m_minMemSpinBox && min != observedMinMemory)
|
||||
{
|
||||
observedMinMemory = min;
|
||||
actuallyChanged = true;
|
||||
if (min > max)
|
||||
{
|
||||
observedMaxMemory = min;
|
||||
m_maxMemSpinBox->setValue(min);
|
||||
}
|
||||
}
|
||||
else if (obj == m_maxMemSpinBox && max != observedMaxMemory)
|
||||
{
|
||||
observedMaxMemory = max;
|
||||
actuallyChanged = true;
|
||||
if (min > max)
|
||||
{
|
||||
observedMinMemory = max;
|
||||
m_minMemSpinBox->setValue(max);
|
||||
}
|
||||
}
|
||||
else if (obj == m_permGenSpinBox && permgen != observedPermGenMemory)
|
||||
{
|
||||
observedPermGenMemory = permgen;
|
||||
actuallyChanged = true;
|
||||
}
|
||||
if(actuallyChanged)
|
||||
{
|
||||
checkJavaPathOnEdit(m_javaPathTextBox->text());
|
||||
}
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::javaVersionSelected(BaseVersionPtr version)
|
||||
{
|
||||
auto java = std::dynamic_pointer_cast<JavaInstall>(version);
|
||||
if(!java)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto visible = java->id.requiresPermGen();
|
||||
m_labelPermGen->setVisible(visible);
|
||||
m_permGenSpinBox->setVisible(visible);
|
||||
m_javaPathTextBox->setText(java->path);
|
||||
checkJavaPath(java->path);
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::on_javaBrowseBtn_clicked()
|
||||
{
|
||||
QString filter;
|
||||
#if defined Q_OS_WIN32
|
||||
filter = "Java (javaw.exe)";
|
||||
#else
|
||||
filter = "Java (java)";
|
||||
#endif
|
||||
QString raw_path = QFileDialog::getOpenFileName(this, tr("Find Java executable"), QString(), filter);
|
||||
if(raw_path.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
QString cooked_path = FS::NormalizePath(raw_path);
|
||||
m_javaPathTextBox->setText(cooked_path);
|
||||
checkJavaPath(cooked_path);
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::on_javaStatusBtn_clicked()
|
||||
{
|
||||
QString text;
|
||||
bool failed = false;
|
||||
switch(javaStatus)
|
||||
{
|
||||
case JavaStatus::NotSet:
|
||||
checkJavaPath(m_javaPathTextBox->text());
|
||||
return;
|
||||
case JavaStatus::DoesNotExist:
|
||||
text += QObject::tr("The specified file either doesn't exist or is not a proper executable.");
|
||||
failed = true;
|
||||
break;
|
||||
case JavaStatus::DoesNotStart:
|
||||
{
|
||||
text += QObject::tr("The specified java binary didn't start properly.<br />");
|
||||
auto htmlError = m_result.errorLog;
|
||||
if(!htmlError.isEmpty())
|
||||
{
|
||||
htmlError.replace('\n', "<br />");
|
||||
text += QString("<font color=\"red\">%1</font>").arg(htmlError);
|
||||
}
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
case JavaStatus::ReturnedInvalidData:
|
||||
{
|
||||
text += QObject::tr("The specified java binary returned unexpected results:<br />");
|
||||
auto htmlOut = m_result.outLog;
|
||||
if(!htmlOut.isEmpty())
|
||||
{
|
||||
htmlOut.replace('\n', "<br />");
|
||||
text += QString("<font color=\"red\">%1</font>").arg(htmlOut);
|
||||
}
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
case JavaStatus::Good:
|
||||
text += QObject::tr("Java test succeeded!<br />Platform reported: %1<br />Java version "
|
||||
"reported: %2<br />").arg(m_result.realPlatform, m_result.javaVersion.toString());
|
||||
break;
|
||||
case JavaStatus::Pending:
|
||||
// TODO: abort here?
|
||||
return;
|
||||
}
|
||||
CustomMessageBox::selectable(
|
||||
this,
|
||||
failed ? QObject::tr("Java test success") : QObject::tr("Java test failure"),
|
||||
text,
|
||||
failed ? QMessageBox::Critical : QMessageBox::Information
|
||||
)->show();
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::setJavaStatus(JavaSettingsWidget::JavaStatus status)
|
||||
{
|
||||
javaStatus = status;
|
||||
switch(javaStatus)
|
||||
{
|
||||
case JavaStatus::Good:
|
||||
m_javaStatusBtn->setIcon(goodIcon);
|
||||
break;
|
||||
case JavaStatus::NotSet:
|
||||
case JavaStatus::Pending:
|
||||
m_javaStatusBtn->setIcon(yellowIcon);
|
||||
break;
|
||||
default:
|
||||
m_javaStatusBtn->setIcon(badIcon);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::javaPathEdited(const QString& path)
|
||||
{
|
||||
checkJavaPathOnEdit(path);
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::checkJavaPathOnEdit(const QString& path)
|
||||
{
|
||||
auto realPath = FS::ResolveExecutable(path);
|
||||
QFileInfo pathInfo(realPath);
|
||||
if (pathInfo.baseName().toLower().contains("java"))
|
||||
{
|
||||
checkJavaPath(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!m_checker)
|
||||
{
|
||||
setJavaStatus(JavaStatus::NotSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::checkJavaPath(const QString &path)
|
||||
{
|
||||
if(m_checker)
|
||||
{
|
||||
queuedCheck = path;
|
||||
return;
|
||||
}
|
||||
auto realPath = FS::ResolveExecutable(path);
|
||||
if(realPath.isNull())
|
||||
{
|
||||
setJavaStatus(JavaStatus::DoesNotExist);
|
||||
return;
|
||||
}
|
||||
setJavaStatus(JavaStatus::Pending);
|
||||
m_checker.reset(new JavaChecker());
|
||||
m_checker->m_path = path;
|
||||
m_checker->m_minMem = m_minMemSpinBox->value();
|
||||
m_checker->m_maxMem = m_maxMemSpinBox->value();
|
||||
if(m_permGenSpinBox->isVisible())
|
||||
{
|
||||
m_checker->m_permGen = m_permGenSpinBox->value();
|
||||
}
|
||||
connect(m_checker.get(), &JavaChecker::checkFinished, this, &JavaSettingsWidget::checkFinished);
|
||||
m_checker->performCheck();
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::checkFinished(JavaCheckResult result)
|
||||
{
|
||||
m_result = result;
|
||||
switch(result.validity)
|
||||
{
|
||||
case JavaCheckResult::Validity::Valid:
|
||||
{
|
||||
setJavaStatus(JavaStatus::Good);
|
||||
break;
|
||||
}
|
||||
case JavaCheckResult::Validity::ReturnedInvalidData:
|
||||
{
|
||||
setJavaStatus(JavaStatus::ReturnedInvalidData);
|
||||
break;
|
||||
}
|
||||
case JavaCheckResult::Validity::Errored:
|
||||
{
|
||||
setJavaStatus(JavaStatus::DoesNotStart);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_checker.reset();
|
||||
if(!queuedCheck.isNull())
|
||||
{
|
||||
checkJavaPath(queuedCheck);
|
||||
queuedCheck.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void JavaSettingsWidget::retranslate()
|
||||
{
|
||||
m_memoryGroupBox->setTitle(tr("Memory"));
|
||||
m_maxMemSpinBox->setToolTip(tr("The maximum amount of memory Minecraft is allowed to use."));
|
||||
m_labelMinMem->setText(tr("Minimum memory allocation:"));
|
||||
m_labelMaxMem->setText(tr("Maximum memory allocation:"));
|
||||
m_minMemSpinBox->setToolTip(tr("The amount of memory Minecraft is started with."));
|
||||
m_permGenSpinBox->setToolTip(tr("The amount of memory available to store loaded Java classes."));
|
||||
m_javaBrowseBtn->setText(tr("Browse"));
|
||||
}
|
102
launcher/widgets/JavaSettingsWidget.h
Normal file
102
launcher/widgets/JavaSettingsWidget.h
Normal file
@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
|
||||
#include <java/JavaChecker.h>
|
||||
#include <BaseVersion.h>
|
||||
#include <QObjectPtr.h>
|
||||
#include <QIcon>
|
||||
|
||||
class QLineEdit;
|
||||
class VersionSelectWidget;
|
||||
class QSpinBox;
|
||||
class QPushButton;
|
||||
class QVBoxLayout;
|
||||
class QHBoxLayout;
|
||||
class QGroupBox;
|
||||
class QGridLayout;
|
||||
class QLabel;
|
||||
class QToolButton;
|
||||
|
||||
/**
|
||||
* This is a widget for all the Java settings dialogs and pages.
|
||||
*/
|
||||
class JavaSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit JavaSettingsWidget(QWidget *parent);
|
||||
virtual ~JavaSettingsWidget() {};
|
||||
|
||||
enum class JavaStatus
|
||||
{
|
||||
NotSet,
|
||||
Pending,
|
||||
Good,
|
||||
DoesNotExist,
|
||||
DoesNotStart,
|
||||
ReturnedInvalidData
|
||||
} javaStatus = JavaStatus::NotSet;
|
||||
|
||||
enum class ValidationStatus
|
||||
{
|
||||
Bad,
|
||||
JavaBad,
|
||||
AllOK
|
||||
};
|
||||
|
||||
void refresh();
|
||||
void initialize();
|
||||
ValidationStatus validate();
|
||||
void retranslate();
|
||||
|
||||
bool permGenEnabled() const;
|
||||
int permGenSize() const;
|
||||
int minHeapSize() const;
|
||||
int maxHeapSize() const;
|
||||
QString javaPath() const;
|
||||
|
||||
|
||||
protected slots:
|
||||
void memoryValueChanged(int);
|
||||
void javaPathEdited(const QString &path);
|
||||
void javaVersionSelected(BaseVersionPtr version);
|
||||
void on_javaBrowseBtn_clicked();
|
||||
void on_javaStatusBtn_clicked();
|
||||
void checkFinished(JavaCheckResult result);
|
||||
|
||||
protected: /* methods */
|
||||
void checkJavaPathOnEdit(const QString &path);
|
||||
void checkJavaPath(const QString &path);
|
||||
void setJavaStatus(JavaStatus status);
|
||||
void setupUi();
|
||||
|
||||
private: /* data */
|
||||
VersionSelectWidget *m_versionWidget = nullptr;
|
||||
QVBoxLayout *m_verticalLayout = nullptr;
|
||||
|
||||
QLineEdit * m_javaPathTextBox = nullptr;
|
||||
QPushButton * m_javaBrowseBtn = nullptr;
|
||||
QToolButton * m_javaStatusBtn = nullptr;
|
||||
QHBoxLayout *m_horizontalLayout = nullptr;
|
||||
|
||||
QGroupBox *m_memoryGroupBox = nullptr;
|
||||
QGridLayout *m_gridLayout_2 = nullptr;
|
||||
QSpinBox *m_maxMemSpinBox = nullptr;
|
||||
QLabel *m_labelMinMem = nullptr;
|
||||
QLabel *m_labelMaxMem = nullptr;
|
||||
QSpinBox *m_minMemSpinBox = nullptr;
|
||||
QLabel *m_labelPermGen = nullptr;
|
||||
QSpinBox *m_permGenSpinBox = nullptr;
|
||||
QIcon goodIcon;
|
||||
QIcon yellowIcon;
|
||||
QIcon badIcon;
|
||||
|
||||
int observedMinMemory = 0;
|
||||
int observedMaxMemory = 0;
|
||||
int observedPermGenMemory = 0;
|
||||
QString queuedCheck;
|
||||
uint64_t m_availableMemory = 0ull;
|
||||
shared_qobject_ptr<JavaChecker> m_checker;
|
||||
JavaCheckResult m_result;
|
||||
};
|
115
launcher/widgets/LabeledToolButton.cpp
Normal file
115
launcher/widgets/LabeledToolButton.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QResizeEvent>
|
||||
#include <QStyleOption>
|
||||
#include "LabeledToolButton.h"
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
|
||||
/*
|
||||
*
|
||||
* Tool Button with a label on it, instead of the normal text rendering
|
||||
*
|
||||
*/
|
||||
|
||||
LabeledToolButton::LabeledToolButton(QWidget * parent)
|
||||
: QToolButton(parent)
|
||||
, m_label(new QLabel(this))
|
||||
{
|
||||
//QToolButton::setText(" ");
|
||||
m_label->setWordWrap(true);
|
||||
m_label->setMouseTracking(false);
|
||||
m_label->setAlignment(Qt::AlignCenter);
|
||||
m_label->setTextInteractionFlags(Qt::NoTextInteraction);
|
||||
// somehow, this makes word wrap work in the QLabel. yay.
|
||||
//m_label->setMinimumWidth(100);
|
||||
}
|
||||
|
||||
QString LabeledToolButton::text() const
|
||||
{
|
||||
return m_label->text();
|
||||
}
|
||||
|
||||
void LabeledToolButton::setText(const QString & text)
|
||||
{
|
||||
m_label->setText(text);
|
||||
}
|
||||
|
||||
void LabeledToolButton::setIcon(QIcon icon)
|
||||
{
|
||||
m_icon = icon;
|
||||
resetIcon();
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
QSize LabeledToolButton::sizeHint() const
|
||||
{
|
||||
/*
|
||||
Q_D(const QToolButton);
|
||||
if (d->sizeHint.isValid())
|
||||
return d->sizeHint;
|
||||
*/
|
||||
ensurePolished();
|
||||
|
||||
int w = 0, h = 0;
|
||||
QStyleOptionToolButton opt;
|
||||
initStyleOption(&opt);
|
||||
QSize sz =m_label->sizeHint();
|
||||
w = sz.width();
|
||||
h = sz.height();
|
||||
|
||||
opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
|
||||
if (popupMode() == MenuButtonPopup)
|
||||
w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);
|
||||
|
||||
QSize rawSize = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this);
|
||||
QSize sizeHint = rawSize.expandedTo(QApplication::globalStrut());
|
||||
return sizeHint;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LabeledToolButton::resizeEvent(QResizeEvent * event)
|
||||
{
|
||||
m_label->setGeometry(QRect(4, 4, width()-8, height()-8));
|
||||
if(!m_icon.isNull())
|
||||
{
|
||||
resetIcon();
|
||||
}
|
||||
QWidget::resizeEvent(event);
|
||||
}
|
||||
|
||||
void LabeledToolButton::resetIcon()
|
||||
{
|
||||
auto iconSz = m_icon.actualSize(QSize(160, 80));
|
||||
float w = iconSz.width();
|
||||
float h = iconSz.height();
|
||||
float ar = w/h;
|
||||
// FIXME: hardcoded max size of 160x80
|
||||
int newW = 80 * ar;
|
||||
if(newW > 160)
|
||||
newW = 160;
|
||||
QSize newSz (newW, 80);
|
||||
auto pixmap = m_icon.pixmap(newSz);
|
||||
m_label->setPixmap(pixmap);
|
||||
m_label->setMinimumHeight(80);
|
||||
m_label->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred );
|
||||
}
|
40
launcher/widgets/LabeledToolButton.h
Normal file
40
launcher/widgets/LabeledToolButton.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
|
||||
class QLabel;
|
||||
|
||||
class LabeledToolButton : public QToolButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QLabel * m_label;
|
||||
QIcon m_icon;
|
||||
|
||||
public:
|
||||
LabeledToolButton(QWidget * parent = 0);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString & text);
|
||||
void setIcon(QIcon icon);
|
||||
virtual QSize sizeHint() const;
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent * event);
|
||||
void resetIcon();
|
||||
};
|
66
launcher/widgets/LanguageSelectionWidget.cpp
Normal file
66
launcher/widgets/LanguageSelectionWidget.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include "LanguageSelectionWidget.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QTreeView>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include "MultiMC.h"
|
||||
#include "translations/TranslationsModel.h"
|
||||
|
||||
LanguageSelectionWidget::LanguageSelectionWidget(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
verticalLayout = new QVBoxLayout(this);
|
||||
verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||
languageView = new QTreeView(this);
|
||||
languageView->setObjectName(QStringLiteral("languageView"));
|
||||
languageView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
languageView->setAlternatingRowColors(true);
|
||||
languageView->setRootIsDecorated(false);
|
||||
languageView->setItemsExpandable(false);
|
||||
languageView->setWordWrap(true);
|
||||
languageView->header()->setCascadingSectionResizes(true);
|
||||
languageView->header()->setStretchLastSection(false);
|
||||
verticalLayout->addWidget(languageView);
|
||||
helpUsLabel = new QLabel(this);
|
||||
helpUsLabel->setObjectName(QStringLiteral("helpUsLabel"));
|
||||
helpUsLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
|
||||
helpUsLabel->setOpenExternalLinks(true);
|
||||
helpUsLabel->setWordWrap(true);
|
||||
verticalLayout->addWidget(helpUsLabel);
|
||||
|
||||
auto translations = MMC->translations();
|
||||
auto index = translations->selectedIndex();
|
||||
languageView->setModel(translations.get());
|
||||
languageView->setCurrentIndex(index);
|
||||
languageView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
languageView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
connect(languageView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &LanguageSelectionWidget::languageRowChanged);
|
||||
verticalLayout->setContentsMargins(0,0,0,0);
|
||||
}
|
||||
|
||||
QString LanguageSelectionWidget::getSelectedLanguageKey() const
|
||||
{
|
||||
auto translations = MMC->translations();
|
||||
return translations->data(languageView->currentIndex(), Qt::UserRole).toString();
|
||||
}
|
||||
|
||||
void LanguageSelectionWidget::retranslate()
|
||||
{
|
||||
QString text = tr("Don't see your language or the quality is poor?<br/><a href=\"%1\">Help us with translations!</a>")
|
||||
.arg("https://github.com/MultiMC/MultiMC5/wiki/Translating-MultiMC");
|
||||
helpUsLabel->setText(text);
|
||||
|
||||
}
|
||||
|
||||
void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, const QModelIndex& previous)
|
||||
{
|
||||
if (current == previous)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto translations = MMC->translations();
|
||||
QString key = translations->data(current, Qt::UserRole).toString();
|
||||
translations->selectLanguage(key);
|
||||
translations->updateLanguage(key);
|
||||
}
|
41
launcher/widgets/LanguageSelectionWidget.h
Normal file
41
launcher/widgets/LanguageSelectionWidget.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QVBoxLayout;
|
||||
class QTreeView;
|
||||
class QLabel;
|
||||
|
||||
class LanguageSelectionWidget: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LanguageSelectionWidget(QWidget *parent = 0);
|
||||
virtual ~LanguageSelectionWidget() { };
|
||||
|
||||
QString getSelectedLanguageKey() const;
|
||||
void retranslate();
|
||||
|
||||
protected slots:
|
||||
void languageRowChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
|
||||
private:
|
||||
QVBoxLayout *verticalLayout = nullptr;
|
||||
QTreeView *languageView = nullptr;
|
||||
QLabel *helpUsLabel = nullptr;
|
||||
};
|
37
launcher/widgets/LineSeparator.cpp
Normal file
37
launcher/widgets/LineSeparator.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "LineSeparator.h"
|
||||
|
||||
#include <QStyle>
|
||||
#include <QStyleOption>
|
||||
#include <QLayout>
|
||||
#include <QPainter>
|
||||
|
||||
void LineSeparator::initStyleOption(QStyleOption *option) const
|
||||
{
|
||||
option->initFrom(this);
|
||||
// in a horizontal layout, the line is vertical (and vice versa)
|
||||
if (m_orientation == Qt::Vertical)
|
||||
option->state |= QStyle::State_Horizontal;
|
||||
}
|
||||
|
||||
LineSeparator::LineSeparator(QWidget *parent, Qt::Orientation orientation)
|
||||
: QWidget(parent), m_orientation(orientation)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
}
|
||||
|
||||
QSize LineSeparator::sizeHint() const
|
||||
{
|
||||
QStyleOption opt;
|
||||
initStyleOption(&opt);
|
||||
const int extent =
|
||||
style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, &opt, parentWidget());
|
||||
return QSize(extent, extent);
|
||||
}
|
||||
|
||||
void LineSeparator::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter p(this);
|
||||
QStyleOption opt;
|
||||
initStyleOption(&opt);
|
||||
style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, &p, parentWidget());
|
||||
}
|
18
launcher/widgets/LineSeparator.h
Normal file
18
launcher/widgets/LineSeparator.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
|
||||
class QStyleOption;
|
||||
|
||||
class LineSeparator : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/// Create a line separator. orientation is the orientation of the line.
|
||||
explicit LineSeparator(QWidget *parent, Qt::Orientation orientation = Qt::Horizontal);
|
||||
QSize sizeHint() const;
|
||||
void paintEvent(QPaintEvent *);
|
||||
void initStyleOption(QStyleOption *option) const;
|
||||
private:
|
||||
Qt::Orientation m_orientation = Qt::Horizontal;
|
||||
};
|
144
launcher/widgets/LogView.cpp
Normal file
144
launcher/widgets/LogView.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
#include "LogView.h"
|
||||
#include <QTextBlock>
|
||||
#include <QScrollBar>
|
||||
|
||||
LogView::LogView(QWidget* parent) : QPlainTextEdit(parent)
|
||||
{
|
||||
setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
||||
m_defaultFormat = new QTextCharFormat(currentCharFormat());
|
||||
}
|
||||
|
||||
LogView::~LogView()
|
||||
{
|
||||
delete m_defaultFormat;
|
||||
}
|
||||
|
||||
void LogView::setWordWrap(bool wrapping)
|
||||
{
|
||||
if(wrapping)
|
||||
{
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setLineWrapMode(QPlainTextEdit::WidgetWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
setLineWrapMode(QPlainTextEdit::NoWrap);
|
||||
}
|
||||
}
|
||||
|
||||
void LogView::setModel(QAbstractItemModel* model)
|
||||
{
|
||||
if(m_model)
|
||||
{
|
||||
disconnect(m_model, &QAbstractItemModel::modelReset, this, &LogView::repopulate);
|
||||
disconnect(m_model, &QAbstractItemModel::rowsInserted, this, &LogView::rowsInserted);
|
||||
disconnect(m_model, &QAbstractItemModel::rowsAboutToBeInserted, this, &LogView::rowsAboutToBeInserted);
|
||||
disconnect(m_model, &QAbstractItemModel::rowsRemoved, this, &LogView::rowsRemoved);
|
||||
}
|
||||
m_model = model;
|
||||
if(m_model)
|
||||
{
|
||||
connect(m_model, &QAbstractItemModel::modelReset, this, &LogView::repopulate);
|
||||
connect(m_model, &QAbstractItemModel::rowsInserted, this, &LogView::rowsInserted);
|
||||
connect(m_model, &QAbstractItemModel::rowsAboutToBeInserted, this, &LogView::rowsAboutToBeInserted);
|
||||
connect(m_model, &QAbstractItemModel::rowsRemoved, this, &LogView::rowsRemoved);
|
||||
connect(m_model, &QAbstractItemModel::destroyed, this, &LogView::modelDestroyed);
|
||||
}
|
||||
repopulate();
|
||||
}
|
||||
|
||||
QAbstractItemModel * LogView::model() const
|
||||
{
|
||||
return m_model;
|
||||
}
|
||||
|
||||
void LogView::modelDestroyed(QObject* model)
|
||||
{
|
||||
if(m_model == model)
|
||||
{
|
||||
setModel(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void LogView::repopulate()
|
||||
{
|
||||
auto doc = document();
|
||||
doc->clear();
|
||||
if(!m_model)
|
||||
{
|
||||
return;
|
||||
}
|
||||
rowsInserted(QModelIndex(), 0, m_model->rowCount() - 1);
|
||||
}
|
||||
|
||||
void LogView::rowsAboutToBeInserted(const QModelIndex& parent, int first, int last)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
Q_UNUSED(first)
|
||||
Q_UNUSED(last)
|
||||
QScrollBar *bar = verticalScrollBar();
|
||||
int max_bar = bar->maximum();
|
||||
int val_bar = bar->value();
|
||||
if (m_scroll)
|
||||
{
|
||||
m_scroll = (max_bar - val_bar) <= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_scroll = val_bar == max_bar;
|
||||
}
|
||||
}
|
||||
|
||||
void LogView::rowsInserted(const QModelIndex& parent, int first, int last)
|
||||
{
|
||||
for(int i = first; i <= last; i++)
|
||||
{
|
||||
auto idx = m_model->index(i, 0, parent);
|
||||
auto text = m_model->data(idx, Qt::DisplayRole).toString();
|
||||
QTextCharFormat format(*m_defaultFormat);
|
||||
auto font = m_model->data(idx, Qt::FontRole);
|
||||
if(font.isValid())
|
||||
{
|
||||
format.setFont(font.value<QFont>());
|
||||
}
|
||||
auto fg = m_model->data(idx, Qt::TextColorRole);
|
||||
if(fg.isValid())
|
||||
{
|
||||
format.setForeground(fg.value<QColor>());
|
||||
}
|
||||
auto bg = m_model->data(idx, Qt::BackgroundRole);
|
||||
if(bg.isValid())
|
||||
{
|
||||
format.setBackground(bg.value<QColor>());
|
||||
}
|
||||
auto workCursor = textCursor();
|
||||
workCursor.movePosition(QTextCursor::End);
|
||||
workCursor.insertText(text, format);
|
||||
workCursor.insertBlock();
|
||||
}
|
||||
if(m_scroll && !m_scrolling)
|
||||
{
|
||||
m_scrolling = true;
|
||||
QMetaObject::invokeMethod( this, "scrollToBottom", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void LogView::rowsRemoved(const QModelIndex& parent, int first, int last)
|
||||
{
|
||||
// TODO: some day... maybe
|
||||
Q_UNUSED(parent)
|
||||
Q_UNUSED(first)
|
||||
Q_UNUSED(last)
|
||||
}
|
||||
|
||||
void LogView::scrollToBottom()
|
||||
{
|
||||
m_scrolling = false;
|
||||
verticalScrollBar()->setSliderPosition(verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
void LogView::findNext(const QString& what, bool reverse)
|
||||
{
|
||||
find(what, reverse ? QTextDocument::FindFlag::FindBackward : QTextDocument::FindFlag(0));
|
||||
}
|
36
launcher/widgets/LogView.h
Normal file
36
launcher/widgets/LogView.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include <QPlainTextEdit>
|
||||
#include <QAbstractItemView>
|
||||
|
||||
class QAbstractItemModel;
|
||||
|
||||
class LogView: public QPlainTextEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LogView(QWidget *parent = nullptr);
|
||||
virtual ~LogView();
|
||||
|
||||
virtual void setModel(QAbstractItemModel *model);
|
||||
QAbstractItemModel *model() const;
|
||||
|
||||
public slots:
|
||||
void setWordWrap(bool wrapping);
|
||||
void findNext(const QString & what, bool reverse);
|
||||
void scrollToBottom();
|
||||
|
||||
protected slots:
|
||||
void repopulate();
|
||||
// note: this supports only appending
|
||||
void rowsInserted(const QModelIndex &parent, int first, int last);
|
||||
void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last);
|
||||
// note: this supports only removing from front
|
||||
void rowsRemoved(const QModelIndex &parent, int first, int last);
|
||||
void modelDestroyed(QObject * model);
|
||||
|
||||
protected:
|
||||
QAbstractItemModel *m_model = nullptr;
|
||||
QTextCharFormat *m_defaultFormat = nullptr;
|
||||
bool m_scroll = false;
|
||||
bool m_scrolling = false;
|
||||
};
|
167
launcher/widgets/MCModInfoFrame.cpp
Normal file
167
launcher/widgets/MCModInfoFrame.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QtGui>
|
||||
|
||||
#include "MCModInfoFrame.h"
|
||||
#include "ui_MCModInfoFrame.h"
|
||||
#include "dialogs/CustomMessageBox.h"
|
||||
|
||||
void MCModInfoFrame::updateWithMod(Mod &m)
|
||||
{
|
||||
if (m.type() == m.MOD_FOLDER)
|
||||
{
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
QString text = "";
|
||||
QString name = "";
|
||||
if (m.name().isEmpty())
|
||||
name = m.mmc_id();
|
||||
else
|
||||
name = m.name();
|
||||
|
||||
if (m.homeurl().isEmpty())
|
||||
text = name;
|
||||
else
|
||||
text = "<a href=\"" + m.homeurl() + "\">" + name + "</a>";
|
||||
if (!m.authors().isEmpty())
|
||||
text += " by " + m.authors().join(", ");
|
||||
|
||||
setModText(text);
|
||||
|
||||
if (m.description().isEmpty())
|
||||
{
|
||||
setModDescription(QString());
|
||||
}
|
||||
else
|
||||
{
|
||||
setModDescription(m.description());
|
||||
}
|
||||
}
|
||||
|
||||
void MCModInfoFrame::clear()
|
||||
{
|
||||
setModText(QString());
|
||||
setModDescription(QString());
|
||||
}
|
||||
|
||||
MCModInfoFrame::MCModInfoFrame(QWidget *parent) :
|
||||
QFrame(parent),
|
||||
ui(new Ui::MCModInfoFrame)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->label_ModDescription->setHidden(true);
|
||||
ui->label_ModText->setHidden(true);
|
||||
updateHiddenState();
|
||||
}
|
||||
|
||||
MCModInfoFrame::~MCModInfoFrame()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MCModInfoFrame::updateHiddenState()
|
||||
{
|
||||
if(ui->label_ModDescription->isHidden() && ui->label_ModText->isHidden())
|
||||
{
|
||||
setHidden(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
setHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MCModInfoFrame::setModText(QString text)
|
||||
{
|
||||
if(text.isEmpty())
|
||||
{
|
||||
ui->label_ModText->setHidden(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->label_ModText->setText(text);
|
||||
ui->label_ModText->setHidden(false);
|
||||
}
|
||||
updateHiddenState();
|
||||
}
|
||||
|
||||
void MCModInfoFrame::setModDescription(QString text)
|
||||
{
|
||||
if(text.isEmpty())
|
||||
{
|
||||
ui->label_ModDescription->setHidden(true);
|
||||
updateHiddenState();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->label_ModDescription->setHidden(false);
|
||||
updateHiddenState();
|
||||
}
|
||||
ui->label_ModDescription->setToolTip("");
|
||||
QString intermediatetext = text.trimmed();
|
||||
bool prev(false);
|
||||
QChar rem('\n');
|
||||
QString finaltext;
|
||||
finaltext.reserve(intermediatetext.size());
|
||||
foreach(const QChar& c, intermediatetext)
|
||||
{
|
||||
if(c == rem && prev){
|
||||
continue;
|
||||
}
|
||||
prev = c == rem;
|
||||
finaltext += c;
|
||||
}
|
||||
QString labeltext;
|
||||
labeltext.reserve(300);
|
||||
if(finaltext.length() > 290)
|
||||
{
|
||||
ui->label_ModDescription->setOpenExternalLinks(false);
|
||||
ui->label_ModDescription->setTextFormat(Qt::TextFormat::RichText);
|
||||
desc = text;
|
||||
// This allows injecting HTML here.
|
||||
labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>");
|
||||
QObject::connect(ui->label_ModDescription, &QLabel::linkActivated, this, &MCModInfoFrame::modDescEllipsisHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->label_ModDescription->setTextFormat(Qt::TextFormat::PlainText);
|
||||
labeltext.append(finaltext);
|
||||
}
|
||||
ui->label_ModDescription->setText(labeltext);
|
||||
}
|
||||
|
||||
void MCModInfoFrame::modDescEllipsisHandler(const QString &link)
|
||||
{
|
||||
if(!currentBox)
|
||||
{
|
||||
currentBox = CustomMessageBox::selectable(this, QString(), desc);
|
||||
connect(currentBox, &QMessageBox::finished, this, &MCModInfoFrame::boxClosed);
|
||||
currentBox->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentBox->setText(desc);
|
||||
}
|
||||
}
|
||||
|
||||
void MCModInfoFrame::boxClosed(int result)
|
||||
{
|
||||
currentBox = nullptr;
|
||||
}
|
52
launcher/widgets/MCModInfoFrame.h
Normal file
52
launcher/widgets/MCModInfoFrame.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include "minecraft/mod/Mod.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MCModInfoFrame;
|
||||
}
|
||||
|
||||
class MCModInfoFrame : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MCModInfoFrame(QWidget *parent = 0);
|
||||
~MCModInfoFrame();
|
||||
|
||||
void setModText(QString text);
|
||||
void setModDescription(QString text);
|
||||
|
||||
void updateWithMod(Mod &m);
|
||||
void clear();
|
||||
|
||||
public slots:
|
||||
void modDescEllipsisHandler(const QString& link );
|
||||
void boxClosed(int result);
|
||||
|
||||
private:
|
||||
void updateHiddenState();
|
||||
|
||||
private:
|
||||
Ui::MCModInfoFrame *ui;
|
||||
QString desc;
|
||||
class QMessageBox * currentBox = nullptr;
|
||||
};
|
||||
|
92
launcher/widgets/MCModInfoFrame.ui
Normal file
92
launcher/widgets/MCModInfoFrame.ui
Normal file
@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MCModInfoFrame</class>
|
||||
<widget class="QFrame" name="MCModInfoFrame">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>527</width>
|
||||
<height>113</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>120</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_ModText">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_ModDescription">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
66
launcher/widgets/ModListView.cpp
Normal file
66
launcher/widgets/ModListView.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ModListView.h"
|
||||
#include <QHeaderView>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QDrag>
|
||||
#include <QRect>
|
||||
|
||||
ModListView::ModListView ( QWidget* parent )
|
||||
:QTreeView ( parent )
|
||||
{
|
||||
setAllColumnsShowFocus ( true );
|
||||
setExpandsOnDoubleClick ( false );
|
||||
setRootIsDecorated ( false );
|
||||
setSortingEnabled ( true );
|
||||
setAlternatingRowColors ( true );
|
||||
setSelectionMode ( QAbstractItemView::ExtendedSelection );
|
||||
setHeaderHidden ( false );
|
||||
setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOn );
|
||||
setHorizontalScrollBarPolicy ( Qt::ScrollBarAsNeeded );
|
||||
setDropIndicatorShown(true);
|
||||
setDragEnabled(true);
|
||||
setDragDropMode(QAbstractItemView::DropOnly);
|
||||
viewport()->setAcceptDrops(true);
|
||||
}
|
||||
|
||||
void ModListView::setModel ( QAbstractItemModel* model )
|
||||
{
|
||||
QTreeView::setModel ( model );
|
||||
auto head = header();
|
||||
head->setStretchLastSection(false);
|
||||
// HACK: this is true for the checkbox column of mod lists
|
||||
auto string = model->headerData(0,head->orientation()).toString();
|
||||
if(head->count() < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(!string.size())
|
||||
{
|
||||
head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||
head->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
for(int i = 2; i < head->count(); i++)
|
||||
head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
|
||||
}
|
||||
else
|
||||
{
|
||||
head->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
for(int i = 1; i < head->count(); i++)
|
||||
head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
|
||||
}
|
||||
}
|
25
launcher/widgets/ModListView.h
Normal file
25
launcher/widgets/ModListView.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <QTreeView>
|
||||
|
||||
class ModListView: public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ModListView ( QWidget* parent = 0 );
|
||||
virtual void setModel ( QAbstractItemModel* model );
|
||||
};
|
239
launcher/widgets/PageContainer.cpp
Normal file
239
launcher/widgets/PageContainer.cpp
Normal file
@ -0,0 +1,239 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "PageContainer.h"
|
||||
|
||||
#include <QStackedLayout>
|
||||
#include <QPushButton>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QUrl>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QListView>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGridLayout>
|
||||
|
||||
#include "MultiMC.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "widgets/IconLabel.h"
|
||||
#include "PageContainer_p.h"
|
||||
#include <MultiMC.h>
|
||||
#include <DesktopServices.h>
|
||||
|
||||
class PageEntryFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
explicit PageEntryFilterModel(QObject *parent = 0) : QSortFilterProxyModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
const QString pattern = filterRegExp().pattern();
|
||||
const auto model = static_cast<PageModel *>(sourceModel());
|
||||
const auto page = model->pages().at(sourceRow);
|
||||
if (!page->shouldDisplay())
|
||||
return false;
|
||||
// Regular contents check, then check page-filter.
|
||||
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
|
||||
}
|
||||
};
|
||||
|
||||
PageContainer::PageContainer(BasePageProvider *pageProvider, QString defaultId,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
createUI();
|
||||
m_model = new PageModel(this);
|
||||
m_proxyModel = new PageEntryFilterModel(this);
|
||||
int counter = 0;
|
||||
auto pages = pageProvider->getPages();
|
||||
for (auto page : pages)
|
||||
{
|
||||
page->stackIndex = m_pageStack->addWidget(dynamic_cast<QWidget *>(page));
|
||||
page->listIndex = counter;
|
||||
page->setParentContainer(this);
|
||||
counter++;
|
||||
}
|
||||
m_model->setPages(pages);
|
||||
|
||||
m_proxyModel->setSourceModel(m_model);
|
||||
m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
||||
m_pageList->setIconSize(QSize(pageIconSize, pageIconSize));
|
||||
m_pageList->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_pageList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
m_pageList->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
|
||||
m_pageList->setModel(m_proxyModel);
|
||||
connect(m_pageList->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)),
|
||||
this, SLOT(currentChanged(QModelIndex)));
|
||||
m_pageStack->setStackingMode(QStackedLayout::StackOne);
|
||||
m_pageList->setFocus();
|
||||
selectPage(defaultId);
|
||||
}
|
||||
|
||||
bool PageContainer::selectPage(QString pageId)
|
||||
{
|
||||
// now find what we want to have selected...
|
||||
auto page = m_model->findPageEntryById(pageId);
|
||||
QModelIndex index;
|
||||
if (page)
|
||||
{
|
||||
index = m_proxyModel->mapFromSource(m_model->index(page->listIndex));
|
||||
}
|
||||
if(!index.isValid())
|
||||
{
|
||||
index = m_proxyModel->index(0, 0);
|
||||
}
|
||||
if (index.isValid())
|
||||
{
|
||||
m_pageList->setCurrentIndex(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PageContainer::refreshContainer()
|
||||
{
|
||||
m_proxyModel->invalidate();
|
||||
if(!m_currentPage->shouldDisplay())
|
||||
{
|
||||
auto index = m_proxyModel->index(0, 0);
|
||||
if(index.isValid())
|
||||
{
|
||||
m_pageList->setCurrentIndex(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: unhandled corner case: what to do when there's no page to select?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PageContainer::createUI()
|
||||
{
|
||||
m_pageStack = new QStackedLayout;
|
||||
m_pageList = new PageView;
|
||||
m_header = new QLabel();
|
||||
m_iconHeader = new IconLabel(this, QIcon(), QSize(24, 24));
|
||||
|
||||
QFont headerLabelFont = m_header->font();
|
||||
headerLabelFont.setBold(true);
|
||||
const int pointSize = headerLabelFont.pointSize();
|
||||
if (pointSize > 0)
|
||||
headerLabelFont.setPointSize(pointSize + 2);
|
||||
m_header->setFont(headerLabelFont);
|
||||
|
||||
QHBoxLayout *headerHLayout = new QHBoxLayout;
|
||||
const int leftMargin = MMC->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
|
||||
headerHLayout->addSpacerItem(new QSpacerItem(leftMargin, 0, QSizePolicy::Fixed, QSizePolicy::Ignored));
|
||||
headerHLayout->addWidget(m_header);
|
||||
headerHLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
|
||||
headerHLayout->addWidget(m_iconHeader);
|
||||
const int rightMargin = MMC->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
|
||||
headerHLayout->addSpacerItem(new QSpacerItem(rightMargin, 0, QSizePolicy::Fixed, QSizePolicy::Ignored));
|
||||
headerHLayout->setContentsMargins(0, 6, 0, 0);
|
||||
|
||||
m_pageStack->setMargin(0);
|
||||
m_pageStack->addWidget(new QWidget(this));
|
||||
|
||||
m_layout = new QGridLayout;
|
||||
m_layout->addLayout(headerHLayout, 0, 1, 1, 1);
|
||||
m_layout->addWidget(m_pageList, 0, 0, 2, 1);
|
||||
m_layout->addLayout(m_pageStack, 1, 1, 1, 1);
|
||||
m_layout->setColumnStretch(1, 4);
|
||||
m_layout->setContentsMargins(0,0,0,6);
|
||||
setLayout(m_layout);
|
||||
}
|
||||
|
||||
void PageContainer::addButtons(QWidget *buttons)
|
||||
{
|
||||
m_layout->addWidget(buttons, 2, 0, 1, 2);
|
||||
}
|
||||
|
||||
void PageContainer::addButtons(QLayout *buttons)
|
||||
{
|
||||
m_layout->addLayout(buttons, 2, 0, 1, 2);
|
||||
}
|
||||
|
||||
void PageContainer::showPage(int row)
|
||||
{
|
||||
if (m_currentPage)
|
||||
{
|
||||
m_currentPage->closed();
|
||||
}
|
||||
if (row != -1)
|
||||
{
|
||||
m_currentPage = m_model->pages().at(row);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentPage = nullptr;
|
||||
}
|
||||
if (m_currentPage)
|
||||
{
|
||||
m_pageStack->setCurrentIndex(m_currentPage->stackIndex);
|
||||
m_header->setText(m_currentPage->displayName());
|
||||
m_iconHeader->setIcon(m_currentPage->icon());
|
||||
m_currentPage->opened();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pageStack->setCurrentIndex(0);
|
||||
m_header->setText(QString());
|
||||
m_iconHeader->setIcon(MMC->getThemedIcon("bug"));
|
||||
}
|
||||
}
|
||||
|
||||
void PageContainer::help()
|
||||
{
|
||||
if (m_currentPage)
|
||||
{
|
||||
QString pageId = m_currentPage->helpPage();
|
||||
if (pageId.isEmpty())
|
||||
return;
|
||||
DesktopServices::openUrl(QUrl("https://github.com/MultiMC/MultiMC5/wiki/" + pageId));
|
||||
}
|
||||
}
|
||||
|
||||
void PageContainer::currentChanged(const QModelIndex ¤t)
|
||||
{
|
||||
showPage(current.isValid() ? m_proxyModel->mapToSource(current).row() : -1);
|
||||
}
|
||||
|
||||
bool PageContainer::prepareToClose()
|
||||
{
|
||||
if(!saveAll())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_currentPage)
|
||||
{
|
||||
m_currentPage->closed();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PageContainer::saveAll()
|
||||
{
|
||||
for (auto page : m_model->pages())
|
||||
{
|
||||
if (!page->apply())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
89
launcher/widgets/PageContainer.h
Normal file
89
launcher/widgets/PageContainer.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QModelIndex>
|
||||
|
||||
#include "pages/BasePageProvider.h"
|
||||
#include "pages/BasePageContainer.h"
|
||||
|
||||
class QLayout;
|
||||
class IconLabel;
|
||||
class QSortFilterProxyModel;
|
||||
class PageModel;
|
||||
class QLabel;
|
||||
class QListView;
|
||||
class QLineEdit;
|
||||
class QStackedLayout;
|
||||
class QGridLayout;
|
||||
|
||||
class PageContainer : public QWidget, public BasePageContainer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PageContainer(BasePageProvider *pageProvider, QString defaultId = QString(),
|
||||
QWidget *parent = 0);
|
||||
virtual ~PageContainer() {}
|
||||
|
||||
void addButtons(QWidget * buttons);
|
||||
void addButtons(QLayout * buttons);
|
||||
/*
|
||||
* Save any unsaved state and prepare to be closed.
|
||||
* @return true if everything can be saved, false if there is something that requires attention
|
||||
*/
|
||||
bool prepareToClose();
|
||||
bool saveAll();
|
||||
|
||||
/* request close - used by individual pages */
|
||||
bool requestClose() override
|
||||
{
|
||||
if(m_container)
|
||||
{
|
||||
return m_container->requestClose();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool selectPage(QString pageId) override;
|
||||
|
||||
void refreshContainer() override;
|
||||
virtual void setParentContainer(BasePageContainer * container)
|
||||
{
|
||||
m_container = container;
|
||||
};
|
||||
|
||||
private:
|
||||
void createUI();
|
||||
|
||||
public slots:
|
||||
void help();
|
||||
|
||||
private slots:
|
||||
void currentChanged(const QModelIndex ¤t);
|
||||
void showPage(int row);
|
||||
|
||||
private:
|
||||
BasePageContainer * m_container = nullptr;
|
||||
BasePage * m_currentPage = 0;
|
||||
QSortFilterProxyModel *m_proxyModel;
|
||||
PageModel *m_model;
|
||||
QStackedLayout *m_pageStack;
|
||||
QListView *m_pageList;
|
||||
QLabel *m_header;
|
||||
IconLabel *m_iconHeader;
|
||||
QGridLayout *m_layout;
|
||||
};
|
123
launcher/widgets/PageContainer_p.h
Normal file
123
launcher/widgets/PageContainer_p.h
Normal file
@ -0,0 +1,123 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QListView>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QEvent>
|
||||
#include <QScrollBar>
|
||||
|
||||
class BasePage;
|
||||
const int pageIconSize = 24;
|
||||
|
||||
class PageViewDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
PageViewDelegate(QObject *parent) : QStyledItemDelegate(parent)
|
||||
{
|
||||
}
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QSize size = QStyledItemDelegate::sizeHint(option, index);
|
||||
size.setHeight(qMax(size.height(), 32));
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
class PageModel : public QAbstractListModel
|
||||
{
|
||||
public:
|
||||
PageModel(QObject *parent = 0) : QAbstractListModel(parent)
|
||||
{
|
||||
QPixmap empty(pageIconSize, pageIconSize);
|
||||
empty.fill(Qt::transparent);
|
||||
m_emptyIcon = QIcon(empty);
|
||||
}
|
||||
virtual ~PageModel() {}
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const
|
||||
{
|
||||
return parent.isValid() ? 0 : m_pages.size();
|
||||
}
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
|
||||
{
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
return m_pages.at(index.row())->displayName();
|
||||
case Qt::DecorationRole:
|
||||
{
|
||||
QIcon icon = m_pages.at(index.row())->icon();
|
||||
if (icon.isNull())
|
||||
icon = m_emptyIcon;
|
||||
// HACK: fixes icon stretching on windows. TODO: report Qt bug for this
|
||||
return QIcon(icon.pixmap(QSize(48,48)));
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void setPages(const QList<BasePage *> &pages)
|
||||
{
|
||||
beginResetModel();
|
||||
m_pages = pages;
|
||||
endResetModel();
|
||||
}
|
||||
const QList<BasePage *> &pages() const
|
||||
{
|
||||
return m_pages;
|
||||
}
|
||||
|
||||
BasePage * findPageEntryById(QString id)
|
||||
{
|
||||
for(auto page: m_pages)
|
||||
{
|
||||
if (page->id() == id)
|
||||
return page;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QList<BasePage *> m_pages;
|
||||
QIcon m_emptyIcon;
|
||||
};
|
||||
|
||||
class PageView : public QListView
|
||||
{
|
||||
public:
|
||||
PageView(QWidget *parent = 0) : QListView(parent)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
|
||||
setItemDelegate(new PageViewDelegate(this));
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
}
|
||||
|
||||
virtual QSize sizeHint() const
|
||||
{
|
||||
int width = sizeHintForColumn(0) + frameWidth() * 2 + 5;
|
||||
if (verticalScrollBar()->isVisible())
|
||||
width += verticalScrollBar()->width();
|
||||
return QSize(width, 100);
|
||||
}
|
||||
|
||||
virtual bool eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (obj == verticalScrollBar() &&
|
||||
(event->type() == QEvent::Show || event->type() == QEvent::Hide))
|
||||
updateGeometry();
|
||||
return QListView::eventFilter(obj, event);
|
||||
}
|
||||
};
|
73
launcher/widgets/ProgressWidget.cpp
Normal file
73
launcher/widgets/ProgressWidget.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
// Licensed under the Apache-2.0 license. See README.md for details.
|
||||
|
||||
#include "ProgressWidget.h"
|
||||
#include <QProgressBar>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QEventLoop>
|
||||
|
||||
#include "tasks/Task.h"
|
||||
|
||||
ProgressWidget::ProgressWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
m_label = new QLabel(this);
|
||||
m_label->setWordWrap(true);
|
||||
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)
|
||||
{
|
||||
if (m_task)
|
||||
{
|
||||
disconnect(m_task.get(), 0, this, 0);
|
||||
}
|
||||
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);
|
||||
if (!m_task->isRunning())
|
||||
{
|
||||
QMetaObject::invokeMethod(m_task.get(), "start", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
bool ProgressWidget::exec(std::shared_ptr<Task> task)
|
||||
{
|
||||
QEventLoop loop;
|
||||
connect(task.get(), &Task::finished, &loop, &QEventLoop::quit);
|
||||
start(task);
|
||||
if (task->isRunning())
|
||||
{
|
||||
loop.exec();
|
||||
}
|
||||
return task->wasSuccessful();
|
||||
}
|
||||
|
||||
void ProgressWidget::handleTaskFinish()
|
||||
{
|
||||
if (!m_task->wasSuccessful())
|
||||
{
|
||||
m_label->setText(m_task->failReason());
|
||||
}
|
||||
}
|
||||
void ProgressWidget::handleTaskStatus(const QString &status)
|
||||
{
|
||||
m_label->setText(status);
|
||||
}
|
||||
void ProgressWidget::handleTaskProgress(qint64 current, qint64 total)
|
||||
{
|
||||
m_bar->setMaximum(total);
|
||||
m_bar->setValue(current);
|
||||
}
|
||||
void ProgressWidget::taskDestroyed()
|
||||
{
|
||||
m_task = nullptr;
|
||||
}
|
32
launcher/widgets/ProgressWidget.h
Normal file
32
launcher/widgets/ProgressWidget.h
Normal file
@ -0,0 +1,32 @@
|
||||
// Licensed under the Apache-2.0 license. See README.md for details.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
|
||||
class Task;
|
||||
class QProgressBar;
|
||||
class QLabel;
|
||||
|
||||
class ProgressWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProgressWidget(QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void start(std::shared_ptr<Task> task);
|
||||
bool exec(std::shared_ptr<Task> task);
|
||||
|
||||
private slots:
|
||||
void handleTaskFinish();
|
||||
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;
|
||||
};
|
179
launcher/widgets/ServerStatus.cpp
Normal file
179
launcher/widgets/ServerStatus.cpp
Normal file
@ -0,0 +1,179 @@
|
||||
#include "ServerStatus.h"
|
||||
#include "LineSeparator.h"
|
||||
#include "IconLabel.h"
|
||||
#include "status/StatusChecker.h"
|
||||
#include <DesktopServices.h>
|
||||
|
||||
#include "MultiMC.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QFrame>
|
||||
#include <QLabel>
|
||||
#include <QMap>
|
||||
#include <QToolButton>
|
||||
#include <QAction>
|
||||
|
||||
class ClickableLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ClickableLabel(QWidget *parent) : QLabel(parent)
|
||||
{
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
}
|
||||
|
||||
~ClickableLabel(){};
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
emit clicked();
|
||||
}
|
||||
};
|
||||
|
||||
class ClickableIconLabel : public IconLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ClickableIconLabel(QWidget *parent, QIcon icon, QSize size) : IconLabel(parent, icon, size)
|
||||
{
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
}
|
||||
|
||||
~ClickableIconLabel(){};
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
emit clicked();
|
||||
}
|
||||
};
|
||||
|
||||
ServerStatus::ServerStatus(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f)
|
||||
{
|
||||
layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
goodIcon = MMC->getThemedIcon("status-good");
|
||||
yellowIcon = MMC->getThemedIcon("status-yellow");
|
||||
badIcon = MMC->getThemedIcon("status-bad");
|
||||
|
||||
addStatus("authserver.mojang.com", tr("Auth"));
|
||||
addLine();
|
||||
addStatus("session.minecraft.net", tr("Session"));
|
||||
addLine();
|
||||
addStatus("textures.minecraft.net", tr("Skins"));
|
||||
addLine();
|
||||
addStatus("api.mojang.com", tr("API"));
|
||||
|
||||
m_statusRefresh = new QToolButton(this);
|
||||
m_statusRefresh->setCheckable(true);
|
||||
m_statusRefresh->setToolButtonStyle(Qt::ToolButtonIconOnly);
|
||||
m_statusRefresh->setIcon(MMC->getThemedIcon("refresh"));
|
||||
layout->addWidget(m_statusRefresh);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
// Start status checker
|
||||
m_statusChecker.reset(new StatusChecker());
|
||||
{
|
||||
auto reloader = m_statusChecker.get();
|
||||
connect(reloader, &StatusChecker::statusChanged, this, &ServerStatus::StatusChanged);
|
||||
connect(reloader, &StatusChecker::statusLoading, this, &ServerStatus::StatusReloading);
|
||||
connect(m_statusRefresh, &QAbstractButton::clicked, this, &ServerStatus::reloadStatus);
|
||||
m_statusChecker->startTimer(60000);
|
||||
reloadStatus();
|
||||
}
|
||||
}
|
||||
|
||||
ServerStatus::~ServerStatus()
|
||||
{
|
||||
}
|
||||
|
||||
void ServerStatus::reloadStatus()
|
||||
{
|
||||
m_statusChecker->reloadStatus();
|
||||
}
|
||||
|
||||
void ServerStatus::addLine()
|
||||
{
|
||||
layout->addWidget(new LineSeparator(this, Qt::Vertical));
|
||||
}
|
||||
|
||||
void ServerStatus::addStatus(QString key, QString name)
|
||||
{
|
||||
{
|
||||
auto label = new ClickableIconLabel(this, badIcon, QSize(16, 16));
|
||||
label->setToolTip(key);
|
||||
serverLabels[key] = label;
|
||||
layout->addWidget(label);
|
||||
connect(label,SIGNAL(clicked()),SLOT(clicked()));
|
||||
}
|
||||
{
|
||||
auto label = new ClickableLabel(this);
|
||||
label->setText(name);
|
||||
label->setToolTip(key);
|
||||
layout->addWidget(label);
|
||||
connect(label,SIGNAL(clicked()),SLOT(clicked()));
|
||||
}
|
||||
}
|
||||
|
||||
void ServerStatus::clicked()
|
||||
{
|
||||
DesktopServices::openUrl(QUrl("https://github.com/MultiMC/MultiMC5/wiki/Mojang-Services-Status"));
|
||||
}
|
||||
|
||||
void ServerStatus::setStatus(QString key, int value)
|
||||
{
|
||||
if (!serverLabels.contains(key))
|
||||
return;
|
||||
IconLabel *label = serverLabels[key];
|
||||
switch(value)
|
||||
{
|
||||
case 0:
|
||||
label->setIcon(goodIcon);
|
||||
break;
|
||||
case 1:
|
||||
label->setIcon(yellowIcon);
|
||||
break;
|
||||
default:
|
||||
case 2:
|
||||
label->setIcon(badIcon);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ServerStatus::StatusChanged(const QMap<QString, QString> statusEntries)
|
||||
{
|
||||
auto convertStatus = [&](QString status)->int
|
||||
{
|
||||
if (status == "green")
|
||||
return 0;
|
||||
else if (status == "yellow")
|
||||
return 1;
|
||||
else if (status == "red")
|
||||
return 2;
|
||||
return 2;
|
||||
}
|
||||
;
|
||||
auto iter = statusEntries.begin();
|
||||
while (iter != statusEntries.end())
|
||||
{
|
||||
QString key = iter.key();
|
||||
auto value = convertStatus(iter.value());
|
||||
setStatus(key, value);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
void ServerStatus::StatusReloading(bool is_reloading)
|
||||
{
|
||||
m_statusRefresh->setChecked(is_reloading);
|
||||
}
|
||||
|
||||
#include "ServerStatus.moc"
|
40
launcher/widgets/ServerStatus.h
Normal file
40
launcher/widgets/ServerStatus.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
#include <QMap>
|
||||
#include <QIcon>
|
||||
#include <memory>
|
||||
|
||||
class IconLabel;
|
||||
class QToolButton;
|
||||
class QHBoxLayout;
|
||||
class StatusChecker;
|
||||
|
||||
class ServerStatus: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServerStatus(QWidget *parent = nullptr, Qt::WindowFlags f = 0);
|
||||
virtual ~ServerStatus();
|
||||
|
||||
public slots:
|
||||
void reloadStatus();
|
||||
void StatusChanged(const QMap<QString, QString> statuses);
|
||||
void StatusReloading(bool is_reloading);
|
||||
|
||||
private slots:
|
||||
void clicked();
|
||||
|
||||
private: /* methods */
|
||||
void addLine();
|
||||
void addStatus(QString key, QString name);
|
||||
void setStatus(QString key, int value);
|
||||
private: /* data */
|
||||
QHBoxLayout * layout = nullptr;
|
||||
QToolButton *m_statusRefresh = nullptr;
|
||||
QMap<QString, IconLabel *> serverLabels;
|
||||
QIcon goodIcon;
|
||||
QIcon yellowIcon;
|
||||
QIcon badIcon;
|
||||
std::shared_ptr<StatusChecker> m_statusChecker;
|
||||
};
|
163
launcher/widgets/VersionListView.cpp
Normal file
163
launcher/widgets/VersionListView.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QApplication>
|
||||
#include <QMouseEvent>
|
||||
#include <QDrag>
|
||||
#include <QPainter>
|
||||
#include "VersionListView.h"
|
||||
#include "Common.h"
|
||||
|
||||
VersionListView::VersionListView(QWidget *parent)
|
||||
:QTreeView ( parent )
|
||||
{
|
||||
m_emptyString = tr("No versions are currently available.");
|
||||
}
|
||||
|
||||
void VersionListView::rowsInserted(const QModelIndex &parent, int start, int end)
|
||||
{
|
||||
m_itemCount += end-start+1;
|
||||
updateEmptyViewPort();
|
||||
QTreeView::rowsInserted(parent, start, end);
|
||||
}
|
||||
|
||||
|
||||
void VersionListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
|
||||
{
|
||||
m_itemCount -= end-start+1;
|
||||
updateEmptyViewPort();
|
||||
QTreeView::rowsInserted(parent, start, end);
|
||||
}
|
||||
|
||||
void VersionListView::setModel(QAbstractItemModel *model)
|
||||
{
|
||||
m_itemCount = model->rowCount();
|
||||
updateEmptyViewPort();
|
||||
QTreeView::setModel(model);
|
||||
}
|
||||
|
||||
void VersionListView::reset()
|
||||
{
|
||||
if(model())
|
||||
{
|
||||
m_itemCount = model()->rowCount();
|
||||
}
|
||||
else {
|
||||
m_itemCount = 0;
|
||||
}
|
||||
updateEmptyViewPort();
|
||||
QTreeView::reset();
|
||||
}
|
||||
|
||||
void VersionListView::setEmptyString(QString emptyString)
|
||||
{
|
||||
m_emptyString = emptyString;
|
||||
updateEmptyViewPort();
|
||||
}
|
||||
|
||||
void VersionListView::setEmptyErrorString(QString emptyErrorString)
|
||||
{
|
||||
m_emptyErrorString = emptyErrorString;
|
||||
updateEmptyViewPort();
|
||||
}
|
||||
|
||||
void VersionListView::setEmptyMode(VersionListView::EmptyMode mode)
|
||||
{
|
||||
m_emptyMode = mode;
|
||||
updateEmptyViewPort();
|
||||
}
|
||||
|
||||
void VersionListView::updateEmptyViewPort()
|
||||
{
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
setAccessibleDescription(currentEmptyString());
|
||||
#endif /* !QT_NO_ACCESSIBILITY */
|
||||
|
||||
if(!m_itemCount)
|
||||
{
|
||||
viewport()->update();
|
||||
}
|
||||
}
|
||||
|
||||
void VersionListView::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
if(m_itemCount)
|
||||
{
|
||||
QTreeView::paintEvent(event);
|
||||
}
|
||||
else
|
||||
{
|
||||
paintInfoLabel(event);
|
||||
}
|
||||
}
|
||||
|
||||
QString VersionListView::currentEmptyString() const
|
||||
{
|
||||
if(m_itemCount) {
|
||||
return QString();
|
||||
}
|
||||
switch(m_emptyMode)
|
||||
{
|
||||
default:
|
||||
case VersionListView::Empty:
|
||||
return QString();
|
||||
case VersionListView::String:
|
||||
return m_emptyString;
|
||||
case VersionListView::ErrorString:
|
||||
return m_emptyErrorString;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VersionListView::paintInfoLabel(QPaintEvent *event) const
|
||||
{
|
||||
QString emptyString = currentEmptyString();
|
||||
|
||||
//calculate the rect for the overlay
|
||||
QPainter painter(viewport());
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
QFont font("sans", 20);
|
||||
font.setBold(true);
|
||||
|
||||
QRect bounds = viewport()->geometry();
|
||||
bounds.moveTop(0);
|
||||
auto innerBounds = bounds;
|
||||
innerBounds.adjust(10, 10, -10, -10);
|
||||
|
||||
QColor background = QApplication::palette().color(QPalette::Foreground);
|
||||
QColor foreground = QApplication::palette().color(QPalette::Base);
|
||||
foreground.setAlpha(190);
|
||||
painter.setFont(font);
|
||||
auto fontMetrics = painter.fontMetrics();
|
||||
auto textRect = fontMetrics.boundingRect(innerBounds, Qt::AlignHCenter | Qt::TextWordWrap, emptyString);
|
||||
textRect.moveCenter(bounds.center());
|
||||
|
||||
auto wrapRect = textRect;
|
||||
wrapRect.adjust(-10, -10, 10, 10);
|
||||
|
||||
//check if we are allowed to draw in our area
|
||||
if (!event->rect().intersects(wrapRect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
painter.setBrush(QBrush(background));
|
||||
painter.setPen(foreground);
|
||||
painter.drawRoundedRect(wrapRect, 5.0, 5.0);
|
||||
|
||||
painter.setPen(foreground);
|
||||
painter.setFont(font);
|
||||
painter.drawText(textRect, Qt::AlignHCenter | Qt::TextWordWrap, emptyString);
|
||||
}
|
56
launcher/widgets/VersionListView.h
Normal file
56
launcher/widgets/VersionListView.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <QTreeView>
|
||||
|
||||
class VersionListView : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
explicit VersionListView(QWidget *parent = 0);
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
virtual void setModel(QAbstractItemModel* model) override;
|
||||
|
||||
enum EmptyMode
|
||||
{
|
||||
Empty,
|
||||
String,
|
||||
ErrorString
|
||||
};
|
||||
|
||||
void setEmptyString(QString emptyString);
|
||||
void setEmptyErrorString(QString emptyErrorString);
|
||||
void setEmptyMode(EmptyMode mode);
|
||||
|
||||
public slots:
|
||||
virtual void reset() override;
|
||||
|
||||
protected slots:
|
||||
virtual void rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end) override;
|
||||
virtual void rowsInserted(const QModelIndex &parent, int start, int end) override;
|
||||
|
||||
private: /* methods */
|
||||
void paintInfoLabel(QPaintEvent *event) const;
|
||||
void updateEmptyViewPort();
|
||||
QString currentEmptyString() const;
|
||||
|
||||
private: /* variables */
|
||||
int m_itemCount = 0;
|
||||
QString m_emptyString;
|
||||
QString m_emptyErrorString;
|
||||
EmptyMode m_emptyMode = Empty;
|
||||
};
|
202
launcher/widgets/VersionSelectWidget.cpp
Normal file
202
launcher/widgets/VersionSelectWidget.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
#include "VersionSelectWidget.h"
|
||||
#include <QProgressBar>
|
||||
#include <QVBoxLayout>
|
||||
#include "VersionListView.h"
|
||||
#include <QHeaderView>
|
||||
#include <VersionProxyModel.h>
|
||||
#include <dialogs/CustomMessageBox.h>
|
||||
|
||||
VersionSelectWidget::VersionSelectWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setObjectName(QStringLiteral("VersionSelectWidget"));
|
||||
verticalLayout = new QVBoxLayout(this);
|
||||
verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||
verticalLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_proxyModel = new VersionProxyModel(this);
|
||||
|
||||
listView = new VersionListView(this);
|
||||
listView->setObjectName(QStringLiteral("listView"));
|
||||
listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
listView->setAlternatingRowColors(true);
|
||||
listView->setRootIsDecorated(false);
|
||||
listView->setItemsExpandable(false);
|
||||
listView->setWordWrap(true);
|
||||
listView->header()->setCascadingSectionResizes(true);
|
||||
listView->header()->setStretchLastSection(false);
|
||||
listView->setModel(m_proxyModel);
|
||||
verticalLayout->addWidget(listView);
|
||||
|
||||
sneakyProgressBar = new QProgressBar(this);
|
||||
sneakyProgressBar->setObjectName(QStringLiteral("sneakyProgressBar"));
|
||||
sneakyProgressBar->setFormat(QStringLiteral("%p%"));
|
||||
verticalLayout->addWidget(sneakyProgressBar);
|
||||
sneakyProgressBar->setHidden(true);
|
||||
connect(listView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &VersionSelectWidget::currentRowChanged);
|
||||
|
||||
QMetaObject::connectSlotsByName(this);
|
||||
}
|
||||
|
||||
void VersionSelectWidget::setCurrentVersion(const QString& version)
|
||||
{
|
||||
m_currentVersion = version;
|
||||
m_proxyModel->setCurrentVersion(version);
|
||||
}
|
||||
|
||||
void VersionSelectWidget::setEmptyString(QString emptyString)
|
||||
{
|
||||
listView->setEmptyString(emptyString);
|
||||
}
|
||||
|
||||
void VersionSelectWidget::setEmptyErrorString(QString emptyErrorString)
|
||||
{
|
||||
listView->setEmptyErrorString(emptyErrorString);
|
||||
}
|
||||
|
||||
VersionSelectWidget::~VersionSelectWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void VersionSelectWidget::setResizeOn(int column)
|
||||
{
|
||||
listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::ResizeToContents);
|
||||
resizeOnColumn = column;
|
||||
listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
|
||||
}
|
||||
|
||||
void VersionSelectWidget::initialize(BaseVersionList *vlist)
|
||||
{
|
||||
m_vlist = vlist;
|
||||
m_proxyModel->setSourceModel(vlist);
|
||||
listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
|
||||
|
||||
if (!m_vlist->isLoaded())
|
||||
{
|
||||
loadList();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_proxyModel->rowCount() == 0)
|
||||
{
|
||||
listView->setEmptyMode(VersionListView::String);
|
||||
}
|
||||
preselect();
|
||||
}
|
||||
}
|
||||
|
||||
void VersionSelectWidget::closeEvent(QCloseEvent * event)
|
||||
{
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
|
||||
void VersionSelectWidget::loadList()
|
||||
{
|
||||
auto newTask = m_vlist->getLoadTask();
|
||||
if (!newTask)
|
||||
{
|
||||
return;
|
||||
}
|
||||
loadTask = newTask.get();
|
||||
connect(loadTask, &Task::succeeded, this, &VersionSelectWidget::onTaskSucceeded);
|
||||
connect(loadTask, &Task::failed, this, &VersionSelectWidget::onTaskFailed);
|
||||
connect(loadTask, &Task::progress, this, &VersionSelectWidget::changeProgress);
|
||||
if(!loadTask->isRunning())
|
||||
{
|
||||
loadTask->start();
|
||||
}
|
||||
sneakyProgressBar->setHidden(false);
|
||||
}
|
||||
|
||||
void VersionSelectWidget::onTaskSucceeded()
|
||||
{
|
||||
if (m_proxyModel->rowCount() == 0)
|
||||
{
|
||||
listView->setEmptyMode(VersionListView::String);
|
||||
}
|
||||
sneakyProgressBar->setHidden(true);
|
||||
preselect();
|
||||
loadTask = nullptr;
|
||||
}
|
||||
|
||||
void VersionSelectWidget::onTaskFailed(const QString& reason)
|
||||
{
|
||||
CustomMessageBox::selectable(this, tr("Error"), tr("List update failed:\n%1").arg(reason), QMessageBox::Warning)->show();
|
||||
onTaskSucceeded();
|
||||
}
|
||||
|
||||
void VersionSelectWidget::changeProgress(qint64 current, qint64 total)
|
||||
{
|
||||
sneakyProgressBar->setMaximum(total);
|
||||
sneakyProgressBar->setValue(current);
|
||||
}
|
||||
|
||||
void VersionSelectWidget::currentRowChanged(const QModelIndex& current, const QModelIndex&)
|
||||
{
|
||||
auto variant = m_proxyModel->data(current, BaseVersionList::VersionPointerRole);
|
||||
emit selectedVersionChanged(variant.value<BaseVersionPtr>());
|
||||
}
|
||||
|
||||
void VersionSelectWidget::preselect()
|
||||
{
|
||||
if(preselectedAlready)
|
||||
return;
|
||||
selectCurrent();
|
||||
if(preselectedAlready)
|
||||
return;
|
||||
selectRecommended();
|
||||
}
|
||||
|
||||
void VersionSelectWidget::selectCurrent()
|
||||
{
|
||||
if(m_currentVersion.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto idx = m_proxyModel->getVersion(m_currentVersion);
|
||||
if(idx.isValid())
|
||||
{
|
||||
preselectedAlready = true;
|
||||
listView->selectionModel()->setCurrentIndex(idx,QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
|
||||
listView->scrollTo(idx, QAbstractItemView::PositionAtCenter);
|
||||
}
|
||||
}
|
||||
|
||||
void VersionSelectWidget::selectRecommended()
|
||||
{
|
||||
auto idx = m_proxyModel->getRecommended();
|
||||
if(idx.isValid())
|
||||
{
|
||||
preselectedAlready = true;
|
||||
listView->selectionModel()->setCurrentIndex(idx,QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
|
||||
listView->scrollTo(idx, QAbstractItemView::PositionAtCenter);
|
||||
}
|
||||
}
|
||||
|
||||
bool VersionSelectWidget::hasVersions() const
|
||||
{
|
||||
return m_proxyModel->rowCount(QModelIndex()) != 0;
|
||||
}
|
||||
|
||||
BaseVersionPtr VersionSelectWidget::selectedVersion() const
|
||||
{
|
||||
auto currentIndex = listView->selectionModel()->currentIndex();
|
||||
auto variant = m_proxyModel->data(currentIndex, BaseVersionList::VersionPointerRole);
|
||||
return variant.value<BaseVersionPtr>();
|
||||
}
|
||||
|
||||
void VersionSelectWidget::setExactFilter(BaseVersionList::ModelRoles role, QString filter)
|
||||
{
|
||||
m_proxyModel->setFilter(role, new ExactFilter(filter));
|
||||
}
|
||||
|
||||
void VersionSelectWidget::setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter)
|
||||
{
|
||||
m_proxyModel->setFilter(role, new ContainsFilter(filter));
|
||||
}
|
||||
|
||||
void VersionSelectWidget::setFilter(BaseVersionList::ModelRoles role, Filter *filter)
|
||||
{
|
||||
m_proxyModel->setFilter(role, filter);
|
||||
}
|
81
launcher/widgets/VersionSelectWidget.h
Normal file
81
launcher/widgets/VersionSelectWidget.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include "BaseVersionList.h"
|
||||
|
||||
class VersionProxyModel;
|
||||
class VersionListView;
|
||||
class QVBoxLayout;
|
||||
class QProgressBar;
|
||||
class Filter;
|
||||
|
||||
class VersionSelectWidget: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VersionSelectWidget(QWidget *parent = 0);
|
||||
~VersionSelectWidget();
|
||||
|
||||
//! loads the list if needed.
|
||||
void initialize(BaseVersionList *vlist);
|
||||
|
||||
//! Starts a task that loads the list.
|
||||
void loadList();
|
||||
|
||||
bool hasVersions() const;
|
||||
BaseVersionPtr selectedVersion() const;
|
||||
void selectRecommended();
|
||||
void selectCurrent();
|
||||
|
||||
void setCurrentVersion(const QString & version);
|
||||
void setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter);
|
||||
void setExactFilter(BaseVersionList::ModelRoles role, QString filter);
|
||||
void setFilter(BaseVersionList::ModelRoles role, Filter *filter);
|
||||
void setEmptyString(QString emptyString);
|
||||
void setEmptyErrorString(QString emptyErrorString);
|
||||
void setResizeOn(int column);
|
||||
|
||||
signals:
|
||||
void selectedVersionChanged(BaseVersionPtr version);
|
||||
|
||||
protected:
|
||||
virtual void closeEvent ( QCloseEvent* );
|
||||
|
||||
private slots:
|
||||
void onTaskSucceeded();
|
||||
void onTaskFailed(const QString &reason);
|
||||
void changeProgress(qint64 current, qint64 total);
|
||||
void currentRowChanged(const QModelIndex ¤t, const QModelIndex &);
|
||||
|
||||
private:
|
||||
void preselect();
|
||||
|
||||
private:
|
||||
QString m_currentVersion;
|
||||
BaseVersionList *m_vlist = nullptr;
|
||||
VersionProxyModel *m_proxyModel = nullptr;
|
||||
int resizeOnColumn = 0;
|
||||
Task * loadTask;
|
||||
bool preselectedAlready = false;
|
||||
|
||||
private:
|
||||
QVBoxLayout *verticalLayout = nullptr;
|
||||
VersionListView *listView = nullptr;
|
||||
QProgressBar *sneakyProgressBar = nullptr;
|
||||
};
|
116
launcher/widgets/WideBar.cpp
Normal file
116
launcher/widgets/WideBar.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
#include "WideBar.h"
|
||||
#include <QToolButton>
|
||||
#include <QMenu>
|
||||
|
||||
class ActionButton : public QToolButton
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ActionButton(QAction * action, QWidget * parent = 0) : QToolButton(parent), m_action(action) {
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
connect(action, &QAction::changed, this, &ActionButton::actionChanged);
|
||||
connect(this, &ActionButton::clicked, action, &QAction::trigger);
|
||||
actionChanged();
|
||||
};
|
||||
private slots:
|
||||
void actionChanged() {
|
||||
setEnabled(m_action->isEnabled());
|
||||
setChecked(m_action->isChecked());
|
||||
setCheckable(m_action->isCheckable());
|
||||
setText(m_action->text());
|
||||
setIcon(m_action->icon());
|
||||
setToolTip(m_action->toolTip());
|
||||
setHidden(!m_action->isVisible());
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
}
|
||||
private:
|
||||
QAction * m_action;
|
||||
};
|
||||
|
||||
|
||||
WideBar::WideBar(const QString& title, QWidget* parent) : QToolBar(title, parent)
|
||||
{
|
||||
setFloatable(false);
|
||||
setMovable(false);
|
||||
}
|
||||
|
||||
WideBar::WideBar(QWidget* parent) : QToolBar(parent)
|
||||
{
|
||||
setFloatable(false);
|
||||
setMovable(false);
|
||||
}
|
||||
|
||||
struct WideBar::BarEntry {
|
||||
enum Type {
|
||||
None,
|
||||
Action,
|
||||
Separator,
|
||||
Spacer
|
||||
} type = None;
|
||||
QAction *qAction = nullptr;
|
||||
QAction *wideAction = nullptr;
|
||||
};
|
||||
|
||||
|
||||
WideBar::~WideBar()
|
||||
{
|
||||
for(auto *iter: m_entries) {
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
|
||||
void WideBar::addAction(QAction* action)
|
||||
{
|
||||
auto entry = new BarEntry();
|
||||
entry->qAction = addWidget(new ActionButton(action, this));
|
||||
entry->wideAction = action;
|
||||
entry->type = BarEntry::Action;
|
||||
m_entries.push_back(entry);
|
||||
}
|
||||
|
||||
void WideBar::addSeparator()
|
||||
{
|
||||
auto entry = new BarEntry();
|
||||
entry->qAction = QToolBar::addSeparator();
|
||||
entry->type = BarEntry::Separator;
|
||||
m_entries.push_back(entry);
|
||||
}
|
||||
|
||||
void WideBar::insertSpacer(QAction* action)
|
||||
{
|
||||
auto iter = std::find_if(m_entries.begin(), m_entries.end(), [action](BarEntry * entry) {
|
||||
return entry->wideAction == action;
|
||||
});
|
||||
if(iter == m_entries.end()) {
|
||||
return;
|
||||
}
|
||||
QWidget* spacer = new QWidget();
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
auto entry = new BarEntry();
|
||||
entry->qAction = insertWidget((*iter)->qAction, spacer);
|
||||
entry->type = BarEntry::Spacer;
|
||||
m_entries.insert(iter, entry);
|
||||
}
|
||||
|
||||
QMenu * WideBar::createContextMenu(QWidget *parent, const QString & title)
|
||||
{
|
||||
QMenu *contextMenu = new QMenu(title, parent);
|
||||
for(auto & item: m_entries) {
|
||||
switch(item->type) {
|
||||
default:
|
||||
case BarEntry::None:
|
||||
break;
|
||||
case BarEntry::Separator:
|
||||
case BarEntry::Spacer:
|
||||
contextMenu->addSeparator();
|
||||
break;
|
||||
case BarEntry::Action:
|
||||
contextMenu->addAction(item->wideAction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return contextMenu;
|
||||
}
|
||||
|
||||
#include "WideBar.moc"
|
26
launcher/widgets/WideBar.h
Normal file
26
launcher/widgets/WideBar.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <QToolBar>
|
||||
#include <QAction>
|
||||
#include <QMap>
|
||||
|
||||
class QMenu;
|
||||
|
||||
class WideBar : public QToolBar
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WideBar(const QString &title, QWidget * parent = nullptr);
|
||||
explicit WideBar(QWidget * parent = nullptr);
|
||||
virtual ~WideBar();
|
||||
|
||||
void addAction(QAction *action);
|
||||
void addSeparator();
|
||||
void insertSpacer(QAction *action);
|
||||
QMenu *createContextMenu(QWidget *parent = nullptr, const QString & title = QString());
|
||||
|
||||
private:
|
||||
struct BarEntry;
|
||||
QList<BarEntry *> m_entries;
|
||||
};
|
Reference in New Issue
Block a user