first attempt at a combobox

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97 2023-10-19 23:53:26 +03:00
parent fea4c4eba8
commit 55946c8923
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318
5 changed files with 280 additions and 5 deletions

View File

@ -1035,6 +1035,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/InstallLoaderDialog.h ui/dialogs/InstallLoaderDialog.h
# GUI - widgets # GUI - widgets
ui/widgets/CheckComboBox.cpp
ui/widgets/CheckComboBox.h
ui/widgets/Common.cpp ui/widgets/Common.cpp
ui/widgets/Common.h ui/widgets/Common.h
ui/widgets/CustomCommands.cpp ui/widgets/CustomCommands.cpp

View File

@ -0,0 +1,205 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* 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/>.
*/
#include "CheckComboBox.h"
#include <qboxlayout.h>
#include <qcontainerfwd.h>
#include <qlistview.h>
#include <QAbstractItemView>
#include <QEvent>
#include <QIdentityProxyModel>
#include <QKeyEvent>
#include <QLineEdit>
#include <QStringList>
#include "BaseVersionList.h"
class CheckComboModel : public QIdentityProxyModel {
Q_OBJECT
public:
explicit CheckComboModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {}
virtual Qt::ItemFlags flags(const QModelIndex& index) const { return QIdentityProxyModel::flags(index) | Qt::ItemIsUserCheckable; }
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
{
if (role == Qt::CheckStateRole) {
auto txt = QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole).toString();
return checked.contains(txt) ? Qt::Checked : Qt::Unchecked;
}
if (role == Qt::DisplayRole)
return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole);
return {};
}
virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole)
{
if (role == Qt::CheckStateRole) {
auto txt = QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole).toString();
if (checked.contains(txt)) {
checked.removeOne(txt);
} else {
checked.push_back(txt);
}
emit dataChanged(index, index);
emit checkStateChanged();
return true;
}
return QIdentityProxyModel::setData(index, value, role);
}
QStringList getChecked() { return checked; }
signals:
void checkStateChanged();
private:
QStringList checked;
};
CheckComboBox::CheckComboBox(QWidget* parent) : QComboBox(parent), m_separator(",")
{
// read-only contents
// QLineEdit* lineEdit = new QLineEdit(this);
// lineEdit->setReadOnly(false);
// setLineEdit(lineEdit);
// lineEdit->disconnect(this);
setInsertPolicy(QComboBox::NoInsert);
view()->installEventFilter(this);
view()->window()->installEventFilter(this);
view()->viewport()->installEventFilter(this);
this->installEventFilter(this);
}
void CheckComboBox::setModel(QAbstractItemModel* new_model)
{
auto proxy = new CheckComboModel(this);
proxy->setSourceModel(new_model);
model()->disconnect(this);
QComboBox::setModel(proxy);
connect(this, QOverload<int>::of(&QComboBox::activated), this, &CheckComboBox::toggleCheckState);
connect(proxy, &CheckComboModel::checkStateChanged, this, &CheckComboBox::updateCheckedItems);
connect(model(), &CheckComboModel::rowsInserted, this, &CheckComboBox::updateCheckedItems);
connect(model(), &CheckComboModel::rowsRemoved, this, &CheckComboBox::updateCheckedItems);
}
void CheckComboBox::hidePopup()
{
if (containerMousePress)
QComboBox::hidePopup();
}
void CheckComboBox::updateCheckedItems()
{
QStringList items = checkedItems();
if (items.isEmpty())
setEditText(defaultText());
else
setEditText(items.join(separator()));
emit checkedItemsChanged(items);
}
QString CheckComboBox::defaultText() const
{
return m_default_text;
}
void CheckComboBox::setDefaultText(const QString& text)
{
if (m_default_text != text) {
m_default_text = text;
updateCheckedItems();
}
}
QString CheckComboBox::separator() const
{
return m_separator;
}
void CheckComboBox::setSeparator(const QString& separator)
{
if (m_separator != separator) {
m_separator = separator;
updateCheckedItems();
}
}
bool CheckComboBox::eventFilter(QObject* receiver, QEvent* event)
{
switch (event->type()) {
case QEvent::KeyPress:
case QEvent::KeyRelease: {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (receiver == this && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)) {
showPopup();
return true;
} else if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Escape) {
// it is important to call QComboBox implementation
QComboBox::hidePopup();
return (keyEvent->key() != Qt::Key_Escape);
}
}
case QEvent::MouseButtonPress:
containerMousePress = (receiver == view()->window());
break;
case QEvent::MouseButtonRelease:
containerMousePress = false;
break;
default:
break;
}
return false;
}
void CheckComboBox::toggleCheckState(int index)
{
QVariant value = itemData(index, Qt::CheckStateRole);
if (value.isValid()) {
Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
setItemData(index, (state == Qt::Unchecked ? Qt::Checked : Qt::Unchecked), Qt::CheckStateRole);
}
updateCheckedItems();
}
Qt::CheckState CheckComboBox::itemCheckState(int index) const
{
return static_cast<Qt::CheckState>(itemData(index, Qt::CheckStateRole).toInt());
}
void CheckComboBox::setItemCheckState(int index, Qt::CheckState state)
{
setItemData(index, state, Qt::CheckStateRole);
}
QStringList CheckComboBox::checkedItems() const
{
if (model())
return dynamic_cast<CheckComboModel*>(model())->getChecked();
return {};
}
void CheckComboBox::setCheckedItems(const QStringList& items)
{
foreach (auto text, items) {
auto index = findText(text);
setItemCheckState(index, index != -1 ? Qt::Checked : Qt::Unchecked);
}
}
#include "CheckComboBox.moc"

View File

@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* 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/>.
*/
#pragma once
#include <qlineedit.h>
#include <QComboBox>
class CheckComboBox : public QComboBox {
Q_OBJECT
public:
explicit CheckComboBox(QWidget* parent = nullptr);
virtual ~CheckComboBox() = default;
virtual void hidePopup() override;
QString defaultText() const;
void setDefaultText(const QString& text);
Qt::CheckState itemCheckState(int index) const;
void setItemCheckState(int index, Qt::CheckState state);
QString separator() const;
void setSeparator(const QString& separator);
QStringList checkedItems() const;
virtual void setModel(QAbstractItemModel* model) override;
public slots:
void setCheckedItems(const QStringList& items);
signals:
void checkedItemsChanged(const QStringList& items);
private:
void updateCheckedItems();
bool eventFilter(QObject* receiver, QEvent* event) override;
void toggleCheckState(int index);
private:
QString m_default_text;
QString m_separator;
bool containerMousePress;
};

View File

@ -105,8 +105,7 @@ void ModFilterWidget::prepareBasicFilter()
m_filter->loaders = loaders; m_filter->loaders = loaders;
auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft"); auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft");
m_filter->versions.push_front(Version{ def }); m_filter->versions.push_front(Version{ def });
m_versions_proxy->setCurrentVersion(def); ui->versionsCb->setCheckedItems({ def });
ui->versionsCb->setCurrentIndex(m_versions_proxy->getVersion(def).row());
} }
void ModFilterWidget::onIncludeSnapshotsChanged() void ModFilterWidget::onIncludeSnapshotsChanged()
@ -119,9 +118,10 @@ void ModFilterWidget::onIncludeSnapshotsChanged()
void ModFilterWidget::onVersionFilterChanged() void ModFilterWidget::onVersionFilterChanged()
{ {
auto version = ui->versionsCb->currentData(BaseVersionList::VersionIdRole).toString(); auto versions = ui->versionsCb->checkedItems();
m_filter->versions.clear(); m_filter->versions.clear();
m_filter->versions.push_front(version); for (auto version : versions)
m_filter->versions.push_back(version);
m_filter_changed = true; m_filter_changed = true;
emit filterChanged(); emit filterChanged();
} }

View File

@ -25,7 +25,7 @@
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="1" column="0"> <item row="1" column="0">
<widget class="QComboBox" name="versionsCb"> <widget class="CheckComboBox" name="versionsCb">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -157,6 +157,13 @@
</layout> </layout>
</widget> </widget>
</widget> </widget>
<customwidgets>
<customwidget>
<class>CheckComboBox</class>
<extends>QComboBox</extends>
<header>ui/widgets/CheckComboBox.h</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>