first attempt at a combobox
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
		| @@ -1035,6 +1035,8 @@ SET(LAUNCHER_SOURCES | ||||
|     ui/dialogs/InstallLoaderDialog.h | ||||
|  | ||||
|     # GUI - widgets | ||||
|     ui/widgets/CheckComboBox.cpp | ||||
|     ui/widgets/CheckComboBox.h | ||||
|     ui/widgets/Common.cpp | ||||
|     ui/widgets/Common.h | ||||
|     ui/widgets/CustomCommands.cpp | ||||
|   | ||||
							
								
								
									
										205
									
								
								launcher/ui/widgets/CheckComboBox.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								launcher/ui/widgets/CheckComboBox.cpp
									
									
									
									
									
										Normal 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" | ||||
							
								
								
									
										61
									
								
								launcher/ui/widgets/CheckComboBox.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								launcher/ui/widgets/CheckComboBox.h
									
									
									
									
									
										Normal 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; | ||||
| }; | ||||
| @@ -105,8 +105,7 @@ void ModFilterWidget::prepareBasicFilter() | ||||
|     m_filter->loaders = loaders; | ||||
|     auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft"); | ||||
|     m_filter->versions.push_front(Version{ def }); | ||||
|     m_versions_proxy->setCurrentVersion(def); | ||||
|     ui->versionsCb->setCurrentIndex(m_versions_proxy->getVersion(def).row()); | ||||
|     ui->versionsCb->setCheckedItems({ def }); | ||||
| } | ||||
|  | ||||
| void ModFilterWidget::onIncludeSnapshotsChanged() | ||||
| @@ -119,9 +118,10 @@ void ModFilterWidget::onIncludeSnapshotsChanged() | ||||
|  | ||||
| void ModFilterWidget::onVersionFilterChanged() | ||||
| { | ||||
|     auto version = ui->versionsCb->currentData(BaseVersionList::VersionIdRole).toString(); | ||||
|     auto versions = ui->versionsCb->checkedItems(); | ||||
|     m_filter->versions.clear(); | ||||
|     m_filter->versions.push_front(version); | ||||
|     for (auto version : versions) | ||||
|         m_filter->versions.push_back(version); | ||||
|     m_filter_changed = true; | ||||
|     emit filterChanged(); | ||||
| } | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
|    </attribute> | ||||
|    <layout class="QGridLayout" name="gridLayout"> | ||||
|     <item row="1" column="0"> | ||||
|      <widget class="QComboBox" name="versionsCb"> | ||||
|      <widget class="CheckComboBox" name="versionsCb"> | ||||
|       <property name="sizePolicy"> | ||||
|        <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|         <horstretch>0</horstretch> | ||||
| @@ -157,6 +157,13 @@ | ||||
|    </layout> | ||||
|   </widget> | ||||
|  </widget> | ||||
|  <customwidgets> | ||||
|   <customwidget> | ||||
|    <class>CheckComboBox</class> | ||||
|    <extends>QComboBox</extends> | ||||
|    <header>ui/widgets/CheckComboBox.h</header> | ||||
|   </customwidget> | ||||
|  </customwidgets> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Trial97
					Trial97