Merge branch 'develop' into feature/java-downloader

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
TheKodeToad
2023-07-16 12:32:31 +01:00
committed by GitHub
879 changed files with 49762 additions and 21479 deletions

View File

@@ -1,52 +1,70 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* 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.
*/
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* 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 <QMessageBox>
#include <QToolTip>
#include "InfoFrame.h"
#include "ui_InfoFrame.h"
#include "ui/dialogs/CustomMessageBox.h"
InfoFrame::InfoFrame(QWidget *parent) :
QFrame(parent),
ui(new Ui::InfoFrame)
void setupLinkToolTip(QLabel* label)
{
QObject::connect(label, &QLabel::linkHovered, [label](const QString& link) {
if (auto url = QUrl(link); !url.isValid() || (url.scheme() != "http" && url.scheme() != "https"))
return;
label->setToolTip(link);
});
}
InfoFrame::InfoFrame(QWidget* parent) : QFrame(parent), ui(new Ui::InfoFrame)
{
ui->setupUi(this);
ui->descriptionLabel->setHidden(true);
ui->nameLabel->setHidden(true);
ui->licenseLabel->setHidden(true);
ui->issueTrackerLabel->setHidden(true);
setupLinkToolTip(ui->iconLabel);
setupLinkToolTip(ui->descriptionLabel);
setupLinkToolTip(ui->nameLabel);
setupLinkToolTip(ui->licenseLabel);
setupLinkToolTip(ui->issueTrackerLabel);
updateHiddenState();
}
@@ -57,38 +75,70 @@ InfoFrame::~InfoFrame()
void InfoFrame::updateWithMod(Mod const& m)
{
if (m.type() == ResourceType::FOLDER)
{
if (m.type() == ResourceType::FOLDER) {
clear();
return;
}
QString text = "";
QString name = "";
QString link = m.metaurl();
if (m.name().isEmpty())
name = m.internal_id();
else
name = m.name();
if (m.homeurl().isEmpty())
if (link.isEmpty())
text = name;
else
text = "<a href=\"" + m.homeurl() + "\">" + name + "</a>";
else {
text = "<a href=\"" + link + "\">" + name + "</a>";
}
if (!m.authors().isEmpty())
text += " by " + m.authors().join(", ");
setName(text);
if (m.description().isEmpty())
{
if (m.description().isEmpty()) {
setDescription(QString());
}
else
{
} else {
setDescription(m.description());
}
setImage();
setImage(m.icon({ 64, 64 }));
auto licenses = m.licenses();
QString licenseText = "";
if (!licenses.empty()) {
for (auto l : licenses) {
if (!licenseText.isEmpty()) {
licenseText += "\n"; // add newline between licenses
}
if (!l.name.isEmpty()) {
if (l.url.isEmpty()) {
licenseText += l.name;
} else {
licenseText += "<a href=\"" + l.url + "\">" + l.name + "</a>";
}
} else if (!l.url.isEmpty()) {
licenseText += "<a href=\"" + l.url + "\">" + l.url + "</a>";
}
if (!l.description.isEmpty() && l.description != l.name) {
licenseText += " " + l.description;
}
}
}
if (!licenseText.isEmpty()) {
setLicense(tr("License: %1").arg(licenseText));
} else {
setLicense();
}
QString issueTracker = "";
if (!m.issueTracker().isEmpty()) {
issueTracker += tr("Report issues to: ");
issueTracker += "<a href=\"" + m.issueTracker() + "\">" + m.issueTracker() + "</a>";
}
setIssueTracker(issueTracker);
}
void InfoFrame::updateWithResource(const Resource& resource)
@@ -97,7 +147,8 @@ void InfoFrame::updateWithResource(const Resource& resource)
setImage();
}
QString InfoFrame::renderColorCodes(QString input) {
QString InfoFrame::renderColorCodes(QString input)
{
// We have to manually set the colors for use.
//
// A color is set using §x, with x = a hex number from 0 to f.
@@ -108,16 +159,12 @@ QString InfoFrame::renderColorCodes(QString input) {
// TODO: Wrap links inside <a> tags
// https://minecraft.fandom.com/wiki/Formatting_codes#Color_codes
const QMap<QChar, QString> color_codes_map = {
{'0', "#000000"}, {'1', "#0000AA"}, {'2', "#00AA00"}, {'3', "#00AAAA"}, {'4', "#AA0000"},
{'5', "#AA00AA"}, {'6', "#FFAA00"}, {'7', "#AAAAAA"}, {'8', "#555555"}, {'9', "#5555FF"},
{'a', "#55FF55"}, {'b', "#55FFFF"}, {'c', "#FF5555"}, {'d', "#FF55FF"}, {'e', "#FFFF55"},
{'f', "#FFFFFF"}
};
const QMap<QChar, QString> color_codes_map = { { '0', "#000000" }, { '1', "#0000AA" }, { '2', "#00AA00" }, { '3', "#00AAAA" },
{ '4', "#AA0000" }, { '5', "#AA00AA" }, { '6', "#FFAA00" }, { '7', "#AAAAAA" },
{ '8', "#555555" }, { '9', "#5555FF" }, { 'a', "#55FF55" }, { 'b', "#55FFFF" },
{ 'c', "#FF5555" }, { 'd', "#FF55FF" }, { 'e', "#FFFF55" }, { 'f', "#FFFFFF" } };
// https://minecraft.fandom.com/wiki/Formatting_codes#Formatting_codes
const QMap<QChar, QString> formatting_codes_map = {
{'l', "b"}, {'m', "s"}, {'n', "u"}, {'o', "i"}
};
const QMap<QChar, QString> formatting_codes_map = { { 'l', "b" }, { 'm', "s" }, { 'n', "u" }, { 'o', "i" } };
QString html("<html>");
QList<QString> tags{};
@@ -162,14 +209,14 @@ void InfoFrame::updateWithResourcePack(ResourcePack& resource_pack)
{
setName(renderColorCodes(resource_pack.name()));
setDescription(renderColorCodes(resource_pack.description()));
setImage(resource_pack.image({64, 64}));
setImage(resource_pack.image({ 64, 64 }));
}
void InfoFrame::updateWithTexturePack(TexturePack& texture_pack)
{
setName(renderColorCodes(texture_pack.name()));
setDescription(renderColorCodes(texture_pack.description()));
setImage(texture_pack.image({64, 64}));
setImage(texture_pack.image({ 64, 64 }));
}
void InfoFrame::clear()
@@ -177,28 +224,25 @@ void InfoFrame::clear()
setName();
setDescription();
setImage();
setLicense();
setIssueTracker();
}
void InfoFrame::updateHiddenState()
{
if(ui->descriptionLabel->isHidden() && ui->nameLabel->isHidden())
{
if (ui->descriptionLabel->isHidden() && ui->nameLabel->isHidden() && ui->licenseLabel->isHidden() &&
ui->issueTrackerLabel->isHidden()) {
setHidden(true);
}
else
{
} else {
setHidden(false);
}
}
void InfoFrame::setName(QString text)
{
if(text.isEmpty())
{
if (text.isEmpty()) {
ui->nameLabel->setHidden(true);
}
else
{
} else {
ui->nameLabel->setText(text);
ui->nameLabel->setHidden(false);
}
@@ -207,14 +251,11 @@ void InfoFrame::setName(QString text)
void InfoFrame::setDescription(QString text)
{
if(text.isEmpty())
{
if (text.isEmpty()) {
ui->descriptionLabel->setHidden(true);
updateHiddenState();
return;
}
else
{
} else {
ui->descriptionLabel->setHidden(false);
updateHiddenState();
}
@@ -224,9 +265,8 @@ void InfoFrame::setDescription(QString text)
QChar rem('\n');
QString finaltext;
finaltext.reserve(intermediatetext.size());
foreach(const QChar& c, intermediatetext)
{
if(c == rem && prev){
foreach (const QChar& c, intermediatetext) {
if (c == rem && prev) {
continue;
}
prev = c == rem;
@@ -234,23 +274,70 @@ void InfoFrame::setDescription(QString text)
}
QString labeltext;
labeltext.reserve(300);
if(finaltext.length() > 290)
{
if (finaltext.length() > 290) {
ui->descriptionLabel->setOpenExternalLinks(false);
ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText);
m_description = text;
// This allows injecting HTML here.
labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>");
QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler);
}
else
{
} else {
ui->descriptionLabel->setTextFormat(Qt::TextFormat::AutoText);
labeltext.append(finaltext);
}
ui->descriptionLabel->setText(labeltext);
}
void InfoFrame::setLicense(QString text)
{
if (text.isEmpty()) {
ui->licenseLabel->setHidden(true);
updateHiddenState();
return;
} else {
ui->licenseLabel->setHidden(false);
updateHiddenState();
}
ui->licenseLabel->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->licenseLabel->setOpenExternalLinks(false);
ui->licenseLabel->setTextFormat(Qt::TextFormat::RichText);
m_description = text;
// This allows injecting HTML here.
labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>");
QObject::connect(ui->licenseLabel, &QLabel::linkActivated, this, &InfoFrame::licenseEllipsisHandler);
} else {
ui->licenseLabel->setTextFormat(Qt::TextFormat::AutoText);
labeltext.append(finaltext);
}
ui->licenseLabel->setText(labeltext);
}
void InfoFrame::setIssueTracker(QString text)
{
if (text.isEmpty()) {
ui->issueTrackerLabel->setHidden(true);
} else {
ui->issueTrackerLabel->setText(text);
ui->issueTrackerLabel->setHidden(false);
}
updateHiddenState();
}
void InfoFrame::setImage(QPixmap img)
{
if (img.isNull()) {
@@ -263,18 +350,26 @@ void InfoFrame::setImage(QPixmap img)
void InfoFrame::descriptionEllipsisHandler(QString link)
{
if(!m_current_box)
{
if (!m_current_box) {
m_current_box = CustomMessageBox::selectable(this, "", m_description);
connect(m_current_box, &QMessageBox::finished, this, &InfoFrame::boxClosed);
m_current_box->show();
}
else
{
} else {
m_current_box->setText(m_description);
}
}
void InfoFrame::licenseEllipsisHandler(QString link)
{
if (!m_current_box) {
m_current_box = CustomMessageBox::selectable(this, "", m_license);
connect(m_current_box, &QMessageBox::finished, this, &InfoFrame::boxClosed);
m_current_box->show();
} else {
m_current_box->setText(m_license);
}
}
void InfoFrame::boxClosed(int result)
{
m_current_box = nullptr;

View File

@@ -1,16 +1,36 @@
/* Copyright 2013-2021 MultiMC Contributors
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
*
* 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
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* http://www.apache.org/licenses/LICENSE-2.0
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* 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
@@ -21,8 +41,7 @@
#include "minecraft/mod/ResourcePack.h"
#include "minecraft/mod/TexturePack.h"
namespace Ui
{
namespace Ui {
class InfoFrame;
}
@@ -36,6 +55,8 @@ class InfoFrame : public QFrame {
void setName(QString text = {});
void setDescription(QString text = {});
void setImage(QPixmap img = {});
void setLicense(QString text = {});
void setIssueTracker(QString text = {});
void clear();
@@ -48,6 +69,7 @@ class InfoFrame : public QFrame {
public slots:
void descriptionEllipsisHandler(QString link);
void licenseEllipsisHandler(QString link);
void boxClosed(int result);
private:
@@ -56,5 +78,6 @@ class InfoFrame : public QFrame {
private:
Ui::InfoFrame* ui;
QString m_description;
QString m_license;
class QMessageBox* m_current_box = nullptr;
};

View File

@@ -35,25 +35,28 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="1">
<widget class="QLabel" name="nameLabel">
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="iconLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
<property name="scaledContents">
<bool>false</bool>
</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 name="margin">
<number>0</number>
</property>
</widget>
</item>
@@ -82,28 +85,69 @@
</property>
</widget>
</item>
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="iconLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<item row="0" column="1">
<widget class="QLabel" name="nameLabel">
<property name="text">
<string notr="true"/>
</property>
<property name="scaledContents">
<bool>false</bool>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="margin">
<number>0</number>
<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 row="2" column="1">
<widget class="QLabel" name="licenseLabel">
<property name="text">
<string/>
</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 row="3" column="1">
<widget class="QLabel" name="issueTrackerLabel">
<property name="text">
<string/>
</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>

View File

@@ -49,7 +49,7 @@ void JavaSettingsWidget::setupUi()
m_verticalLayout = new QVBoxLayout(this);
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
m_versionWidget = new VersionSelectWidget(this);
m_versionWidget = new VersionSelectWidget(true, this);
m_verticalLayout->addWidget(m_versionWidget);
m_horizontalLayout = new QHBoxLayout();
@@ -74,6 +74,7 @@ void JavaSettingsWidget::setupUi()
m_memoryGroupBox->setObjectName(QStringLiteral("memoryGroupBox"));
m_gridLayout_2 = new QGridLayout(m_memoryGroupBox);
m_gridLayout_2->setObjectName(QStringLiteral("gridLayout_2"));
m_gridLayout_2->setColumnStretch(0, 1);
m_labelMinMem = new QLabel(m_memoryGroupBox);
m_labelMinMem->setObjectName(QStringLiteral("labelMinMem"));
@@ -83,7 +84,7 @@ void JavaSettingsWidget::setupUi()
m_minMemSpinBox->setObjectName(QStringLiteral("minMemSpinBox"));
m_minMemSpinBox->setSuffix(QStringLiteral(" MiB"));
m_minMemSpinBox->setMinimum(128);
m_minMemSpinBox->setMaximum(m_availableMemory);
m_minMemSpinBox->setMaximum(1048576);
m_minMemSpinBox->setSingleStep(128);
m_labelMinMem->setBuddy(m_minMemSpinBox);
m_gridLayout_2->addWidget(m_minMemSpinBox, 0, 1, 1, 1);
@@ -96,11 +97,15 @@ void JavaSettingsWidget::setupUi()
m_maxMemSpinBox->setObjectName(QStringLiteral("maxMemSpinBox"));
m_maxMemSpinBox->setSuffix(QStringLiteral(" MiB"));
m_maxMemSpinBox->setMinimum(128);
m_maxMemSpinBox->setMaximum(m_availableMemory);
m_maxMemSpinBox->setMaximum(1048576);
m_maxMemSpinBox->setSingleStep(128);
m_labelMaxMem->setBuddy(m_maxMemSpinBox);
m_gridLayout_2->addWidget(m_maxMemSpinBox, 1, 1, 1, 1);
m_labelMaxMemIcon = new QLabel(m_memoryGroupBox);
m_labelMaxMemIcon->setObjectName(QStringLiteral("labelMaxMemIcon"));
m_gridLayout_2->addWidget(m_labelMaxMemIcon, 1, 2, 1, 1);
m_labelPermGen = new QLabel(m_memoryGroupBox);
m_labelPermGen->setObjectName(QStringLiteral("labelPermGen"));
m_labelPermGen->setText(QStringLiteral("PermGen:"));
@@ -111,7 +116,7 @@ void JavaSettingsWidget::setupUi()
m_permGenSpinBox->setObjectName(QStringLiteral("permGenSpinBox"));
m_permGenSpinBox->setSuffix(QStringLiteral(" MiB"));
m_permGenSpinBox->setMinimum(64);
m_permGenSpinBox->setMaximum(m_availableMemory);
m_permGenSpinBox->setMaximum(1048576);
m_permGenSpinBox->setSingleStep(8);
m_gridLayout_2->addWidget(m_permGenSpinBox, 2, 1, 1, 1);
m_permGenSpinBox->setVisible(false);
@@ -137,6 +142,7 @@ void JavaSettingsWidget::initialize()
m_minMemSpinBox->setValue(observedMinMemory);
m_maxMemSpinBox->setValue(observedMaxMemory);
m_permGenSpinBox->setValue(observedPermGenMemory);
updateThresholds();
}
void JavaSettingsWidget::refresh()
@@ -217,9 +223,9 @@ int JavaSettingsWidget::permGenSize() const
void JavaSettingsWidget::memoryValueChanged(int)
{
bool actuallyChanged = false;
int min = m_minMemSpinBox->value();
int max = m_maxMemSpinBox->value();
int permgen = m_permGenSpinBox->value();
unsigned int min = m_minMemSpinBox->value();
unsigned int max = m_maxMemSpinBox->value();
unsigned int permgen = m_permGenSpinBox->value();
QObject *obj = sender();
if (obj == m_minMemSpinBox && min != observedMinMemory)
{
@@ -249,10 +255,11 @@ void JavaSettingsWidget::memoryValueChanged(int)
if(actuallyChanged)
{
checkJavaPathOnEdit(m_javaPathTextBox->text());
updateThresholds();
}
}
void JavaSettingsWidget::javaVersionSelected(BaseVersionPtr version)
void JavaSettingsWidget::javaVersionSelected(BaseVersion::Ptr version)
{
auto java = std::dynamic_pointer_cast<JavaInstall>(version);
if(!java)
@@ -446,3 +453,26 @@ void JavaSettingsWidget::retranslate()
m_permGenSpinBox->setToolTip(tr("The amount of memory available to store loaded Java classes."));
m_javaBrowseBtn->setText(tr("Browse"));
}
void JavaSettingsWidget::updateThresholds()
{
QString iconName;
if (observedMaxMemory >= m_availableMemory) {
iconName = "status-bad";
m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation exceeds your system memory capacity."));
} else if (observedMaxMemory > (m_availableMemory * 0.9)) {
iconName = "status-yellow";
m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity."));
} else {
iconName = "status-good";
m_labelMaxMemIcon->setToolTip("");
}
{
auto height = m_labelMaxMemIcon->fontInfo().pixelSize();
QIcon icon = APPLICATION->getThemedIcon(iconName);
QPixmap pix = icon.pixmap(height, height);
m_labelMaxMemIcon->setPixmap(pix);
}
}

View File

@@ -56,11 +56,13 @@ public:
int maxHeapSize() const;
QString javaPath() const;
void updateThresholds();
protected slots:
void memoryValueChanged(int);
void javaPathEdited(const QString &path);
void javaVersionSelected(BaseVersionPtr version);
void javaVersionSelected(BaseVersion::Ptr version);
void on_javaBrowseBtn_clicked();
void on_javaStatusBtn_clicked();
void on_javaDownloadBtn_clicked();
@@ -86,6 +88,7 @@ private: /* data */
QSpinBox *m_maxMemSpinBox = nullptr;
QLabel *m_labelMinMem = nullptr;
QLabel *m_labelMaxMem = nullptr;
QLabel *m_labelMaxMemIcon = nullptr;
QSpinBox *m_minMemSpinBox = nullptr;
QLabel *m_labelPermGen = nullptr;
QSpinBox *m_permGenSpinBox = nullptr;
@@ -95,9 +98,9 @@ private: /* data */
QIcon yellowIcon;
QIcon badIcon;
int observedMinMemory = 0;
int observedMaxMemory = 0;
int observedPermGenMemory = 0;
unsigned int observedMinMemory = 0;
unsigned int observedMaxMemory = 0;
unsigned int observedPermGenMemory = 0;
QString queuedCheck;
uint64_t m_availableMemory = 0ull;
shared_qobject_ptr<JavaChecker> m_checker;

View File

@@ -1,16 +1,16 @@
#include "LanguageSelectionWidget.h"
#include <QVBoxLayout>
#include <QTreeView>
#include <QCheckBox>
#include <QHeaderView>
#include <QLabel>
#include <QTreeView>
#include <QVBoxLayout>
#include "Application.h"
#include "BuildConfig.h"
#include "translations/TranslationsModel.h"
#include "settings/Setting.h"
#include "translations/TranslationsModel.h"
LanguageSelectionWidget::LanguageSelectionWidget(QWidget *parent) :
QWidget(parent)
LanguageSelectionWidget::LanguageSelectionWidget(QWidget* parent) : QWidget(parent)
{
verticalLayout = new QVBoxLayout(this);
verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
@@ -31,6 +31,13 @@ LanguageSelectionWidget::LanguageSelectionWidget(QWidget *parent) :
helpUsLabel->setWordWrap(true);
verticalLayout->addWidget(helpUsLabel);
formatCheckbox = new QCheckBox(this);
formatCheckbox->setObjectName(QStringLiteral("formatCheckbox"));
formatCheckbox->setCheckState(APPLICATION->settings()->get("UseSystemLocale").toBool() ? Qt::Checked : Qt::Unchecked);
connect(formatCheckbox, &QCheckBox::stateChanged,
[this]() { APPLICATION->translations()->setUseSystemLocale(formatCheckbox->isChecked()); });
verticalLayout->addWidget(formatCheckbox);
auto translations = APPLICATION->translations();
auto index = translations->selectedIndex();
languageView->setModel(translations.get());
@@ -38,7 +45,7 @@ LanguageSelectionWidget::LanguageSelectionWidget(QWidget *parent) :
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);
verticalLayout->setContentsMargins(0, 0, 0, 0);
auto language_setting = APPLICATION->settings()->getSetting("Language");
connect(language_setting.get(), &Setting::SettingChanged, this, &LanguageSelectionWidget::languageSettingChanged);
@@ -53,15 +60,14 @@ QString LanguageSelectionWidget::getSelectedLanguageKey() const
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(BuildConfig.TRANSLATIONS_URL);
.arg(BuildConfig.TRANSLATIONS_URL);
helpUsLabel->setText(text);
formatCheckbox->setText(tr("Use system locales"));
}
void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, const QModelIndex& previous)
{
if (current == previous)
{
if (current == previous) {
return;
}
auto translations = APPLICATION->translations();
@@ -70,7 +76,7 @@ void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, con
translations->updateLanguage(key);
}
void LanguageSelectionWidget::languageSettingChanged(const Setting &, const QVariant)
void LanguageSelectionWidget::languageSettingChanged(const Setting&, const QVariant)
{
auto translations = APPLICATION->translations();
auto index = translations->selectedIndex();

View File

@@ -21,23 +21,24 @@ class QVBoxLayout;
class QTreeView;
class QLabel;
class Setting;
class QCheckBox;
class LanguageSelectionWidget: public QWidget
{
class LanguageSelectionWidget : public QWidget {
Q_OBJECT
public:
explicit LanguageSelectionWidget(QWidget *parent = 0);
virtual ~LanguageSelectionWidget() { };
public:
explicit LanguageSelectionWidget(QWidget* parent = 0);
virtual ~LanguageSelectionWidget(){};
QString getSelectedLanguageKey() const;
void retranslate();
protected slots:
void languageRowChanged(const QModelIndex &current, const QModelIndex &previous);
void languageSettingChanged(const Setting &, const QVariant);
protected slots:
void languageRowChanged(const QModelIndex& current, const QModelIndex& previous);
void languageSettingChanged(const Setting&, const QVariant);
private:
QVBoxLayout *verticalLayout = nullptr;
QTreeView *languageView = nullptr;
QLabel *helpUsLabel = nullptr;
private:
QVBoxLayout* verticalLayout = nullptr;
QTreeView* languageView = nullptr;
QLabel* helpUsLabel = nullptr;
QCheckBox* formatCheckbox = nullptr;
};

View File

@@ -49,7 +49,7 @@ public:
auto getFilter() -> std::shared_ptr<Filter>;
auto changed() const -> bool { return m_last_version_id != m_version_id; }
Meta::VersionListPtr versionList() { return m_version_list; }
Meta::VersionList::Ptr versionList() { return m_version_list; }
private:
ModFilterWidget(Version def, QWidget* parent = nullptr);
@@ -73,7 +73,7 @@ private:
/* Version stuff */
QButtonGroup m_mcVersion_buttons;
Meta::VersionListPtr m_version_list;
Meta::VersionList::Ptr m_version_list;
/* Used to tell if the filter was changed since the last getFilter() call */
VersionButtonID m_last_version_id = VersionButtonID::Strict;

View File

@@ -31,7 +31,6 @@ ModListView::ModListView ( QWidget* parent )
setSelectionMode ( QAbstractItemView::ExtendedSelection );
setHeaderHidden ( false );
setSelectionBehavior(QAbstractItemView::SelectRows);
setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOn );
setHorizontalScrollBarPolicy ( Qt::ScrollBarAsNeeded );
setDropIndicatorShown(true);
setDragEnabled(true);
@@ -64,3 +63,12 @@ void ModListView::setModel ( QAbstractItemModel* model )
head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
}
}
void ModListView::setResizeModes(const QList<QHeaderView::ResizeMode> &modes)
{
auto head = header();
for(int i = 0; i < modes.count(); i++) {
head->setSectionResizeMode(i, modes[i]);
}
}

View File

@@ -14,6 +14,7 @@
*/
#pragma once
#include <QHeaderView>
#include <QTreeView>
class ModListView: public QTreeView
@@ -22,4 +23,5 @@ class ModListView: public QTreeView
public:
explicit ModListView ( QWidget* parent = 0 );
virtual void setModel ( QAbstractItemModel* model );
virtual void setResizeModes (const QList<QHeaderView::ResizeMode>& modes);
};

View File

@@ -87,10 +87,16 @@ PageContainer::PageContainer(BasePageProvider *pageProvider, QString defaultId,
auto pages = pageProvider->getPages();
for (auto page : pages)
{
page->stackIndex = m_pageStack->addWidget(dynamic_cast<QWidget *>(page));
auto widget = dynamic_cast<QWidget *>(page);
widget->setParent(this);
page->stackIndex = m_pageStack->addWidget(widget);
page->listIndex = counter;
page->setParentContainer(this);
counter++;
page->updateExtraInfo = [this](QString id, QString info) {
if (m_currentPage && id == m_currentPage->id())
m_header->setText(m_currentPage->displayName() + info);
};
}
m_model->setPages(pages);
@@ -130,6 +136,16 @@ bool PageContainer::selectPage(QString pageId)
return false;
}
BasePage* PageContainer::getPage(QString pageId)
{
return m_model->findPageEntryById(pageId);
}
const QList<BasePage*> PageContainer::getPages() const
{
return m_model->pages();
}
void PageContainer::refreshContainer()
{
m_proxyModel->invalidate();

View File

@@ -79,6 +79,8 @@ public:
}
virtual bool selectPage(QString pageId) override;
BasePage* getPage(QString pageId) override;
const QList<BasePage*> getPages() const;
void refreshContainer() override;
virtual void setParentContainer(BasePageContainer * container)

View File

@@ -39,7 +39,7 @@ void ProgressWidget::progressFormat(QString format)
m_bar->setFormat(format);
}
void ProgressWidget::watch(Task* task)
void ProgressWidget::watch(const Task* task)
{
if (!task)
return;
@@ -51,17 +51,21 @@ void ProgressWidget::watch(Task* task)
connect(m_task, &Task::finished, this, &ProgressWidget::handleTaskFinish);
connect(m_task, &Task::status, this, &ProgressWidget::handleTaskStatus);
// TODO: should we connect &Task::details
connect(m_task, &Task::progress, this, &ProgressWidget::handleTaskProgress);
connect(m_task, &Task::destroyed, this, &ProgressWidget::taskDestroyed);
show();
if (m_task->isRunning())
show();
else
connect(m_task, &Task::started, this, &ProgressWidget::show);
}
void ProgressWidget::start(Task* task)
void ProgressWidget::start(const Task* task)
{
watch(task);
if (!m_task->isRunning())
QMetaObject::invokeMethod(m_task, "start", Qt::QueuedConnection);
QMetaObject::invokeMethod(const_cast<Task*>(m_task), "start", Qt::QueuedConnection);
}
bool ProgressWidget::exec(std::shared_ptr<Task> task)

View File

@@ -27,10 +27,10 @@ class ProgressWidget : public QWidget {
public slots:
/** Watch the progress of a task. */
void watch(Task* task);
void watch(const Task* task);
/** Watch the progress of a task, and start it if needed */
void start(Task* task);
void start(const Task* task);
/** Blocking way of waiting for a task to finish. */
bool exec(std::shared_ptr<Task> task);
@@ -50,7 +50,7 @@ class ProgressWidget : public QWidget {
private:
QLabel* m_label = nullptr;
QProgressBar* m_bar = nullptr;
Task* m_task = nullptr;
const Task* m_task = nullptr;
bool m_hide_if_inactive = false;
};

View File

@@ -64,6 +64,17 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o
font.setBold(true);
font.setUnderline(true);
}
if (index.data(UserDataTypes::INSTALLED).toBool()) {
auto hRect = opt.rect;
hRect.setX(hRect.x() + 1);
hRect.setY(hRect.y() + 1);
hRect.setHeight(hRect.height() - 2);
hRect.setWidth(hRect.width() - 2);
// Set nice font
font.setItalic(true);
font.setOverline(true);
painter->drawRect(hRect);
}
font.setPointSize(font.pointSize() + 2);
painter->setFont(font);

View File

@@ -6,7 +6,8 @@
enum UserDataTypes {
TITLE = 257, // QString
DESCRIPTION = 258, // QString
SELECTED = 259 // bool
SELECTED = 259, // bool
INSTALLED = 260 // bool
};
/** This is an item delegate composed of:

View File

@@ -0,0 +1,58 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PrismLaucher - Minecraft Launcher
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "SubTaskProgressBar.h"
#include "ui_SubTaskProgressBar.h"
unique_qobject_ptr<SubTaskProgressBar> SubTaskProgressBar::create(QWidget* parent)
{
auto progress_bar = new SubTaskProgressBar(parent);
return unique_qobject_ptr<SubTaskProgressBar>(progress_bar);
}
SubTaskProgressBar::SubTaskProgressBar(QWidget* parent)
: ui(new Ui::SubTaskProgressBar)
{
ui->setupUi(this);
}
SubTaskProgressBar::~SubTaskProgressBar()
{
delete ui;
}
void SubTaskProgressBar::setRange(int min, int max)
{
ui->progressBar->setRange(min, max);
}
void SubTaskProgressBar::setValue(int value)
{
ui->progressBar->setValue(value);
}
void SubTaskProgressBar::setStatus(QString status)
{
ui->statusLabel->setText(status);
}
void SubTaskProgressBar::setDetails(QString details)
{
ui->statusDetailsLabel->setText(details);
}

View File

@@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PrismLaucher - Minecraft Launcher
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <QWidget>
#include "QObjectPtr.h"
namespace Ui {
class SubTaskProgressBar;
}
class SubTaskProgressBar : public QWidget
{
Q_OBJECT
public:
static unique_qobject_ptr<SubTaskProgressBar> create(QWidget* parent = nullptr);
SubTaskProgressBar(QWidget* parent = nullptr);
~SubTaskProgressBar();
void setRange(int min, int max);
void setValue(int value);
void setStatus(QString status);
void setDetails(QString details);
private:
Ui::SubTaskProgressBar* ui;
};

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SubTaskProgressBar</class>
<widget class="QWidget" name="SubTaskProgressBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>312</width>
<height>86</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
<property name="spacing">
<number>8</number>
</property>
<item>
<widget class="QLabel" name="statusLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="text">
<string>Sub Task Status...</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statusDetailsLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="text">
<string>Status Details</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="value">
<number>24</number>
</property>
<property name="textVisible">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,150 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Tayou <tayou@gmx.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ThemeCustomizationWidget.h"
#include "ui_ThemeCustomizationWidget.h"
#include "Application.h"
#include "ui/themes/ITheme.h"
#include "ui/themes/ThemeManager.h"
ThemeCustomizationWidget::ThemeCustomizationWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ThemeCustomizationWidget)
{
ui->setupUi(this);
loadSettings();
connect(ui->iconsComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyIconTheme);
connect(ui->widgetStyleComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyWidgetTheme);
connect(ui->backgroundCatComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeCustomizationWidget::applyCatTheme);
}
ThemeCustomizationWidget::~ThemeCustomizationWidget()
{
delete ui;
}
/// <summary>
/// The layout was not quite right, so currently this just disables the UI elements, which should be hidden instead
/// TODO FIXME
///
/// Original Method One:
/// ui->iconsComboBox->setVisible(features& ThemeFields::ICONS);
/// ui->iconsLabel->setVisible(features& ThemeFields::ICONS);
/// ui->widgetStyleComboBox->setVisible(features& ThemeFields::WIDGETS);
/// ui->widgetThemeLabel->setVisible(features& ThemeFields::WIDGETS);
/// ui->backgroundCatComboBox->setVisible(features& ThemeFields::CAT);
/// ui->backgroundCatLabel->setVisible(features& ThemeFields::CAT);
///
/// original Method Two:
/// if (!(features & ThemeFields::ICONS)) {
/// ui->formLayout->setRowVisible(0, false);
/// }
/// if (!(features & ThemeFields::WIDGETS)) {
/// ui->formLayout->setRowVisible(1, false);
/// }
/// if (!(features & ThemeFields::CAT)) {
/// ui->formLayout->setRowVisible(2, false);
/// }
/// </summary>
/// <param name="features"></param>
void ThemeCustomizationWidget::showFeatures(ThemeFields features) {
ui->iconsComboBox->setEnabled(features & ThemeFields::ICONS);
ui->iconsLabel->setEnabled(features & ThemeFields::ICONS);
ui->widgetStyleComboBox->setEnabled(features & ThemeFields::WIDGETS);
ui->widgetThemeLabel->setEnabled(features & ThemeFields::WIDGETS);
ui->backgroundCatComboBox->setEnabled(features & ThemeFields::CAT);
ui->backgroundCatLabel->setEnabled(features & ThemeFields::CAT);
}
void ThemeCustomizationWidget::applyIconTheme(int index) {
auto settings = APPLICATION->settings();
auto originalIconTheme = settings->get("IconTheme").toString();
auto& newIconTheme = m_iconThemeOptions[index].first;
settings->set("IconTheme", newIconTheme);
if (originalIconTheme != newIconTheme) {
APPLICATION->applyCurrentlySelectedTheme();
}
emit currentIconThemeChanged(index);
}
void ThemeCustomizationWidget::applyWidgetTheme(int index) {
auto settings = APPLICATION->settings();
auto originalAppTheme = settings->get("ApplicationTheme").toString();
auto newAppTheme = ui->widgetStyleComboBox->currentData().toString();
if (originalAppTheme != newAppTheme) {
settings->set("ApplicationTheme", newAppTheme);
APPLICATION->applyCurrentlySelectedTheme();
}
emit currentWidgetThemeChanged(index);
}
void ThemeCustomizationWidget::applyCatTheme(int index) {
auto settings = APPLICATION->settings();
settings->set("BackgroundCat", m_catOptions[index].first);
emit currentCatChanged(index);
}
void ThemeCustomizationWidget::applySettings()
{
applyIconTheme(ui->iconsComboBox->currentIndex());
applyWidgetTheme(ui->widgetStyleComboBox->currentIndex());
applyCatTheme(ui->backgroundCatComboBox->currentIndex());
}
void ThemeCustomizationWidget::loadSettings()
{
auto settings = APPLICATION->settings();
auto iconTheme = settings->get("IconTheme").toString();
for (auto& iconThemeFromList : m_iconThemeOptions) {
QIcon iconForComboBox = QIcon(QString(":/icons/%1/scalable/settings").arg(iconThemeFromList.first));
ui->iconsComboBox->addItem(iconForComboBox, iconThemeFromList.second);
if (iconTheme == iconThemeFromList.first) {
ui->iconsComboBox->setCurrentIndex(ui->iconsComboBox->count() - 1);
}
}
{
auto currentTheme = settings->get("ApplicationTheme").toString();
auto themes = APPLICATION->getValidApplicationThemes();
int idx = 0;
for (auto& theme : themes) {
ui->widgetStyleComboBox->addItem(theme->name(), theme->id());
if (currentTheme == theme->id()) {
ui->widgetStyleComboBox->setCurrentIndex(idx);
}
idx++;
}
}
auto cat = settings->get("BackgroundCat").toString();
for (auto& catFromList : m_catOptions) {
QIcon catIcon = QIcon(QString(":/backgrounds/%1").arg(ThemeManager::getCatImage(catFromList.first)));
ui->backgroundCatComboBox->addItem(catIcon, catFromList.second);
if (cat == catFromList.first) {
ui->backgroundCatComboBox->setCurrentIndex(ui->backgroundCatComboBox->count() - 1);
}
}
}
void ThemeCustomizationWidget::retranslate()
{
ui->retranslateUi(this);
}

View File

@@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Tayou <tayou@gmx.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QWidget>
#include "translations/TranslationsModel.h"
enum ThemeFields { NONE = 0b0000, ICONS = 0b0001, WIDGETS = 0b0010, CAT = 0b0100 };
namespace Ui {
class ThemeCustomizationWidget;
}
class ThemeCustomizationWidget : public QWidget {
Q_OBJECT
public:
explicit ThemeCustomizationWidget(QWidget* parent = nullptr);
~ThemeCustomizationWidget();
void showFeatures(ThemeFields features);
void applySettings();
void loadSettings();
void retranslate();
private slots:
void applyIconTheme(int index);
void applyWidgetTheme(int index);
void applyCatTheme(int index);
signals:
int currentIconThemeChanged(int index);
int currentWidgetThemeChanged(int index);
int currentCatChanged(int index);
private:
Ui::ThemeCustomizationWidget* ui;
//TODO finish implementing
QList<std::pair<QString, QString>> m_iconThemeOptions{
{ "pe_colored", QObject::tr("Simple (Colored Icons)") },
{ "pe_light", QObject::tr("Simple (Light Icons)") },
{ "pe_dark", QObject::tr("Simple (Dark Icons)") },
{ "pe_blue", QObject::tr("Simple (Blue Icons)") },
{ "breeze_light", QObject::tr("Breeze Light") },
{ "breeze_dark", QObject::tr("Breeze Dark") },
{ "OSX", QObject::tr("OSX") },
{ "iOS", QObject::tr("iOS") },
{ "flat", QObject::tr("Flat") },
{ "flat_white", QObject::tr("Flat (White)") },
{ "multimc", QObject::tr("Legacy") },
{ "custom", QObject::tr("Custom") }
};
QList<std::pair<QString, QString>> m_catOptions{
{ "kitteh", QObject::tr("Background Cat (from MultiMC)") },
{ "rory", QObject::tr("Rory ID 11 (drawn by Ashtaka)") },
{ "rory-flat", QObject::tr("Rory ID 11 (flat edition, drawn by Ashtaka)") },
{ "teawie", QObject::tr("Teawie (drawn by SympathyTea)") }
};
};

View File

@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ThemeCustomizationWidget</class>
<widget class="QWidget" name="ThemeCustomizationWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>191</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</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 row="0" column="0">
<widget class="QLabel" name="iconsLabel">
<property name="text">
<string>&amp;Icons</string>
</property>
<property name="buddy">
<cstring>iconsComboBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="iconsComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="widgetThemeLabel">
<property name="text">
<string>&amp;Colors</string>
</property>
<property name="buddy">
<cstring>widgetStyleComboBox</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="widgetStyleComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="backgroundCatLabel">
<property name="toolTip">
<string>The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar.</string>
</property>
<property name="text">
<string>C&amp;at</string>
</property>
<property name="buddy">
<cstring>backgroundCatComboBox</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="backgroundCatComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar.</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="catInfoLabel">
<property name="toolTip">
<string>The cat appears in the background and is not shown by default. It is only made visible when pressing the Cat button in the Toolbar.</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="about">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -101,7 +101,7 @@ void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source,
auto full_entry_path = entry->getFullPath();
auto source_url = source;
connect(job, &NetJob::succeeded, [this, doc, full_entry_path, source_url, posInDocument] {
connect(job, &NetJob::succeeded, this, [this, doc, full_entry_path, source_url, posInDocument] {
qDebug() << "Loaded resource at" << full_entry_path;
// If we flushed, don't proceed.

View File

@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -125,14 +126,9 @@ void VersionListView::paintEvent(QPaintEvent *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:

View File

@@ -1,15 +1,20 @@
#include "VersionSelectWidget.h"
#include <QApplication>
#include <QEvent>
#include <QHeaderView>
#include <QKeyEvent>
#include <QProgressBar>
#include <QVBoxLayout>
#include <QHeaderView>
#include "VersionProxyModel.h"
#include "ui/dialogs/CustomMessageBox.h"
VersionSelectWidget::VersionSelectWidget(QWidget* parent)
: QWidget(parent)
VersionSelectWidget::VersionSelectWidget(QWidget* parent) : VersionSelectWidget(false, parent) {}
VersionSelectWidget::VersionSelectWidget(bool focusSearch, QWidget* parent)
: QWidget(parent), focusSearch(focusSearch)
{
setObjectName(QStringLiteral("VersionSelectWidget"));
verticalLayout = new QVBoxLayout(this);
@@ -30,6 +35,21 @@ VersionSelectWidget::VersionSelectWidget(QWidget* parent)
listView->setModel(m_proxyModel);
verticalLayout->addWidget(listView);
search = new QLineEdit(this);
search->setPlaceholderText(tr("Search"));
search->setClearButtonEnabled(true);
verticalLayout->addWidget(search);
connect(search, &QLineEdit::textEdited, [this](const QString& value) {
m_proxyModel->setSearch(value);
if (!value.isEmpty() || !listView->selectionModel()->hasSelection()) {
const QModelIndex first = listView->model()->index(0, 0);
listView->selectionModel()->setCurrentIndex(first, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
listView->scrollToTop();
} else
listView->scrollTo(listView->selectionModel()->currentIndex(), QAbstractItemView::PositionAtCenter);
});
search->installEventFilter(this);
sneakyProgressBar = new QProgressBar(this);
sneakyProgressBar->setObjectName(QStringLiteral("sneakyProgressBar"));
sneakyProgressBar->setFormat(QStringLiteral("%p%"));
@@ -72,6 +92,23 @@ void VersionSelectWidget::setResizeOn(int column)
listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
}
bool VersionSelectWidget::eventFilter(QObject *watched, QEvent *event) {
if (watched == search && event->type() == QEvent::KeyPress) {
const QKeyEvent* keyEvent = (QKeyEvent*)event;
const bool up = keyEvent->key() == Qt::Key_Up;
const bool down = keyEvent->key() == Qt::Key_Down;
if (up || down) {
const QModelIndex index = listView->model()->index(listView->currentIndex().row() + (up ? -1 : 1), 0);
if (index.row() >= 0 && index.row() < listView->model()->rowCount()) {
listView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
return true;
}
}
}
return QObject::eventFilter(watched, event);
}
void VersionSelectWidget::initialize(BaseVersionList *vlist)
{
m_vlist = vlist;
@@ -79,6 +116,9 @@ void VersionSelectWidget::initialize(BaseVersionList *vlist)
listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
if (focusSearch)
search->setFocus();
if (!m_vlist->isLoaded())
{
loadList();
@@ -142,7 +182,7 @@ void VersionSelectWidget::changeProgress(qint64 current, qint64 total)
void VersionSelectWidget::currentRowChanged(const QModelIndex& current, const QModelIndex&)
{
auto variant = m_proxyModel->data(current, BaseVersionList::VersionPointerRole);
emit selectedVersionChanged(variant.value<BaseVersionPtr>());
emit selectedVersionChanged(variant.value<BaseVersion::Ptr>());
}
void VersionSelectWidget::preselect()
@@ -186,11 +226,11 @@ bool VersionSelectWidget::hasVersions() const
return m_proxyModel->rowCount(QModelIndex()) != 0;
}
BaseVersionPtr VersionSelectWidget::selectedVersion() const
BaseVersion::Ptr VersionSelectWidget::selectedVersion() const
{
auto currentIndex = listView->selectionModel()->currentIndex();
auto variant = m_proxyModel->data(currentIndex, BaseVersionList::VersionPointerRole);
return variant.value<BaseVersionPtr>();
return variant.value<BaseVersion::Ptr>();
}
void VersionSelectWidget::setExactFilter(BaseVersionList::ModelRoles role, QString filter)

View File

@@ -1,22 +1,43 @@
/* Copyright 2013-2021 MultiMC Contributors
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
*
* 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
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* http://www.apache.org/licenses/LICENSE-2.0
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* 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 <QLineEdit>
#include "BaseVersionList.h"
#include "VersionListView.h"
@@ -30,7 +51,8 @@ class VersionSelectWidget: public QWidget
{
Q_OBJECT
public:
explicit VersionSelectWidget(QWidget *parent = 0);
explicit VersionSelectWidget(QWidget *parent);
explicit VersionSelectWidget(bool focusSearch = false, QWidget *parent = 0);
~VersionSelectWidget();
//! loads the list if needed.
@@ -40,7 +62,7 @@ public:
void loadList();
bool hasVersions() const;
BaseVersionPtr selectedVersion() const;
BaseVersion::Ptr selectedVersion() const;
void selectRecommended();
void selectCurrent();
@@ -52,9 +74,10 @@ public:
void setEmptyErrorString(QString emptyErrorString);
void setEmptyMode(VersionListView::EmptyMode mode);
void setResizeOn(int column);
bool eventFilter(QObject* watched, QEvent* event) override;
signals:
void selectedVersionChanged(BaseVersionPtr version);
void selectedVersionChanged(BaseVersion::Ptr version);
protected:
virtual void closeEvent ( QCloseEvent* );
@@ -75,9 +98,10 @@ private:
int resizeOnColumn = 0;
Task * loadTask;
bool preselectedAlready = false;
bool focusSearch;
private:
QVBoxLayout *verticalLayout = nullptr;
VersionListView *listView = nullptr;
QLineEdit *search;
QProgressBar *sneakyProgressBar = nullptr;
};

View File

@@ -1,159 +1,310 @@
#include "WideBar.h"
#include <QToolButton>
#include <QMenu>
class ActionButton : public QToolButton
{
#include <QContextMenuEvent>
#include <QCryptographicHash>
#include <QToolButton>
class ActionButton : public QToolButton {
Q_OBJECT
public:
ActionButton(QAction * action, QWidget * parent = 0) : QToolButton(parent), m_action(action) {
public:
ActionButton(QAction* action, QWidget* parent = nullptr, bool use_default_action = false) : QToolButton(parent),
m_action(action), m_use_default_action(use_default_action)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
// workaround for breeze and breeze forks
setProperty("_kde_toolButton_alignment", Qt::AlignLeft);
if (m_use_default_action) {
setDefaultAction(action);
} else {
connect(this, &ActionButton::clicked, action, &QAction::trigger);
}
connect(action, &QAction::changed, this, &ActionButton::actionChanged);
connect(this, &ActionButton::clicked, action, &QAction::trigger);
actionChanged();
};
private slots:
void actionChanged() {
public 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());
// better pop up mode
if (m_action->menu()) {
setPopupMode(QToolButton::MenuButtonPopup);
}
if (!m_use_default_action) {
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;
};
private:
QAction* m_action;
bool m_use_default_action;
};
WideBar::WideBar(const QString& title, QWidget* parent) : QToolBar(title, parent)
{
setFloatable(false);
setMovable(false);
setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
connect(this, &QToolBar::customContextMenuRequested, this, &WideBar::showVisibilityMenu);
}
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;
}
setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
connect(this, &QToolBar::customContextMenuRequested, this, &WideBar::showVisibilityMenu);
}
void WideBar::addAction(QAction* action)
{
auto entry = new BarEntry();
entry->qAction = addWidget(new ActionButton(action, this));
entry->wideAction = action;
entry->type = BarEntry::Action;
BarEntry entry;
entry.bar_action = addWidget(new ActionButton(action, this, m_use_default_action));
entry.menu_action = action;
entry.type = BarEntry::Type::Action;
m_entries.push_back(entry);
m_menu_state = MenuState::Dirty;
}
void WideBar::addSeparator()
{
auto entry = new BarEntry();
entry->qAction = QToolBar::addSeparator();
entry->type = BarEntry::Separator;
BarEntry entry;
entry.bar_action = QToolBar::addSeparator();
entry.type = BarEntry::Type::Separator;
m_entries.push_back(entry);
}
auto WideBar::getMatching(QAction* act) -> QList<BarEntry*>::iterator
auto WideBar::getMatching(QAction* act) -> QList<BarEntry>::iterator
{
auto iter = std::find_if(m_entries.begin(), m_entries.end(), [act](BarEntry * entry) {
return entry->wideAction == act;
});
auto iter = std::find_if(m_entries.begin(), m_entries.end(), [act](BarEntry const& entry) { return entry.menu_action == act; });
return iter;
}
void WideBar::insertActionBefore(QAction* before, QAction* action){
void WideBar::insertActionBefore(QAction* before, QAction* action)
{
auto iter = getMatching(before);
if(iter == m_entries.end())
if (iter == m_entries.end())
return;
auto entry = new BarEntry();
entry->qAction = insertWidget((*iter)->qAction, new ActionButton(action, this));
entry->wideAction = action;
entry->type = BarEntry::Action;
BarEntry entry;
entry.bar_action = insertWidget(iter->bar_action, new ActionButton(action, this, m_use_default_action));
entry.menu_action = action;
entry.type = BarEntry::Type::Action;
m_entries.insert(iter, entry);
m_menu_state = MenuState::Dirty;
}
void WideBar::insertActionAfter(QAction* after, QAction* action){
void WideBar::insertActionAfter(QAction* after, QAction* action)
{
auto iter = getMatching(after);
if(iter == m_entries.end())
if (iter == m_entries.end())
return;
auto entry = new BarEntry();
entry->qAction = insertWidget((*(iter+1))->qAction, new ActionButton(action, this));
entry->wideAction = action;
entry->type = BarEntry::Action;
m_entries.insert(iter + 1, entry);
iter++;
// the action to insert after is present
// however, the element after it isn't valid
if (iter == m_entries.end()) {
// append the action instead of inserting it
addAction(action);
return;
}
BarEntry entry;
entry.bar_action = insertWidget(iter->bar_action, new ActionButton(action, this, m_use_default_action));
entry.menu_action = action;
entry.type = BarEntry::Type::Action;
m_entries.insert(iter, entry);
m_menu_state = MenuState::Dirty;
}
void WideBar::insertWidgetBefore(QAction* before, QWidget* widget)
{
auto iter = getMatching(before);
if (iter == m_entries.end())
return;
insertWidget(iter->bar_action, widget);
}
void WideBar::insertSpacer(QAction* action)
{
auto iter = getMatching(action);
if(iter == m_entries.end())
if (iter == m_entries.end())
return;
QWidget* spacer = new QWidget();
auto* spacer = new QWidget();
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
auto entry = new BarEntry();
entry->qAction = insertWidget((*iter)->qAction, spacer);
entry->type = BarEntry::Spacer;
BarEntry entry;
entry.bar_action = insertWidget(iter->bar_action, spacer);
entry.type = BarEntry::Type::Spacer;
m_entries.insert(iter, entry);
}
void WideBar::insertSeparator(QAction* before)
{
auto iter = getMatching(before);
if(iter == m_entries.end())
if (iter == m_entries.end())
return;
auto entry = new BarEntry();
entry->qAction = QToolBar::insertSeparator(before);
entry->type = BarEntry::Separator;
BarEntry entry;
entry.bar_action = QToolBar::insertSeparator(iter->bar_action);
entry.type = BarEntry::Type::Separator;
m_entries.insert(iter, entry);
}
QMenu * WideBar::createContextMenu(QWidget *parent, const QString & title)
QMenu* WideBar::createContextMenu(QWidget* parent, const QString& title)
{
QMenu *contextMenu = new QMenu(title, parent);
for(auto & item: m_entries) {
switch(item->type) {
auto* contextMenu = new QMenu(title, parent);
for (auto& item : m_entries) {
switch (item.type) {
default:
case BarEntry::None:
case BarEntry::Type::None:
break;
case BarEntry::Separator:
case BarEntry::Spacer:
case BarEntry::Type::Separator:
case BarEntry::Type::Spacer:
contextMenu->addSeparator();
break;
case BarEntry::Action:
contextMenu->addAction(item->wideAction);
case BarEntry::Type::Action:
contextMenu->addAction(item.menu_action);
break;
}
}
return contextMenu;
}
static void copyAction(QAction* from, QAction* to)
{
Q_ASSERT(from);
Q_ASSERT(to);
to->setText(from->text());
to->setIcon(from->icon());
to->setToolTip(from->toolTip());
}
void WideBar::showVisibilityMenu(QPoint const& position)
{
if (!m_bar_menu)
m_bar_menu = std::make_unique<QMenu>(this);
if (m_menu_state == MenuState::Dirty) {
for (auto* old_action : m_bar_menu->actions())
old_action->deleteLater();
m_bar_menu->clear();
m_bar_menu->addActions(m_context_menu_actions);
m_bar_menu->addSeparator()->setText(tr("Customize toolbar actions"));
for (auto& entry : m_entries) {
if (entry.type != BarEntry::Type::Action)
continue;
auto act = new QAction();
copyAction(entry.menu_action, act);
act->setCheckable(true);
act->setChecked(entry.bar_action->isVisible());
connect(act, &QAction::toggled, entry.bar_action, [this, &entry](bool toggled){
entry.bar_action->setVisible(toggled);
// NOTE: This is needed so that disabled actions get reflected on the button when it is made visible.
static_cast<ActionButton*>(widgetForAction(entry.bar_action))->actionChanged();
});
m_bar_menu->addAction(act);
}
m_menu_state = MenuState::Fresh;
}
m_bar_menu->popup(mapToGlobal(position));
}
void WideBar::addContextMenuAction(QAction* action) {
m_context_menu_actions.append(action);
}
[[nodiscard]] QByteArray WideBar::getVisibilityState() const
{
QByteArray state;
for (auto const& entry : m_entries) {
if (entry.type != BarEntry::Type::Action)
continue;
state.append(entry.bar_action->isVisible() ? '1' : '0');
}
state.append(',');
state.append(getHash());
return state;
}
void WideBar::setVisibilityState(QByteArray&& state)
{
auto split = state.split(',');
auto bits = split.first();
auto hash = split.last();
// If the actions changed, we better not try to load the old one to avoid unwanted hiding
if (!checkHash(hash))
return;
qsizetype i = 0;
for (auto& entry : m_entries) {
if (entry.type != BarEntry::Type::Action)
continue;
if (i == bits.size())
break;
entry.bar_action->setVisible(bits.at(i++) == '1');
// NOTE: This is needed so that disabled actions get reflected on the button when it is made visible.
static_cast<ActionButton*>(widgetForAction(entry.bar_action))->actionChanged();
}
}
QByteArray WideBar::getHash() const
{
QCryptographicHash hash(QCryptographicHash::Sha1);
for (auto const& entry : m_entries) {
if (entry.type != BarEntry::Type::Action)
continue;
hash.addData(entry.menu_action->text().toLatin1());
}
return hash.result().toBase64();
}
bool WideBar::checkHash(QByteArray const& old_hash) const
{
return old_hash == getHash();
}
#include "WideBar.moc"

View File

@@ -2,17 +2,21 @@
#include <QAction>
#include <QMap>
#include <QMenu>
#include <QToolBar>
class QMenu;
#include <memory>
class WideBar : public QToolBar {
Q_OBJECT
// Why: so we can enable / disable alt shortcuts in toolbuttons
// with toolbuttons using setDefaultAction, theres no alt shortcuts
Q_PROPERTY(bool useDefaultAction MEMBER m_use_default_action)
public:
explicit WideBar(const QString& title, QWidget* parent = nullptr);
explicit WideBar(QWidget* parent = nullptr);
virtual ~WideBar();
~WideBar() override = default;
void addAction(QAction* action);
void addSeparator();
@@ -21,14 +25,40 @@ class WideBar : public QToolBar {
void insertSeparator(QAction* before);
void insertActionBefore(QAction* before, QAction* action);
void insertActionAfter(QAction* after, QAction* action);
void insertWidgetBefore(QAction* before, QWidget* widget);
QMenu* createContextMenu(QWidget* parent = nullptr, const QString& title = QString());
void showVisibilityMenu(const QPoint&);
void addContextMenuAction(QAction* action);
// Ideally we would use a QBitArray for this, but it doesn't support string conversion,
// so using it in settings is very messy.
[[nodiscard]] QByteArray getVisibilityState() const;
void setVisibilityState(QByteArray&&);
private:
struct BarEntry;
struct BarEntry {
enum class Type { None, Action, Separator, Spacer } type = Type::None;
QAction* bar_action = nullptr;
QAction* menu_action = nullptr;
};
auto getMatching(QAction* act) -> QList<BarEntry*>::iterator;
auto getMatching(QAction* act) -> QList<BarEntry>::iterator;
/** Used to distinguish between versions of the WideBar with different actions */
[[nodiscard]] QByteArray getHash() const;
[[nodiscard]] bool checkHash(QByteArray const&) const;
private:
QList<BarEntry*> m_entries;
QList<BarEntry> m_entries;
QList<QAction*> m_context_menu_actions;
bool m_use_default_action = false;
// Menu to toggle visibility from buttons in the bar
std::unique_ptr<QMenu> m_bar_menu = nullptr;
enum class MenuState { Fresh, Dirty } m_menu_state = MenuState::Dirty;
};