diff options
Diffstat (limited to 'archived/projt-launcher/launcher/ui/dialogs/IconPickerDialog.cpp')
| -rw-r--r-- | archived/projt-launcher/launcher/ui/dialogs/IconPickerDialog.cpp | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/archived/projt-launcher/launcher/ui/dialogs/IconPickerDialog.cpp b/archived/projt-launcher/launcher/ui/dialogs/IconPickerDialog.cpp new file mode 100644 index 0000000000..450d1235ea --- /dev/null +++ b/archived/projt-launcher/launcher/ui/dialogs/IconPickerDialog.cpp @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0 +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * Copyright (C) 2026 Project Tick + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * === Upstream License Block (Do Not Modify) ============================== + * + * 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 <QFileDialog> +#include <QKeyEvent> +#include <QLineEdit> +#include <QPushButton> +#include <QSortFilterProxyModel> + +#include "Application.h" + +#include "IconPickerDialog.h" +#include "ui_IconPickerDialog.h" + +#include "ui/instanceview/InstanceDelegate.h" + +#include <DesktopServices.h> +#include "icons/IconList.hpp" +#include "icons/IconUtils.hpp" + +IconPickerDialog::IconPickerDialog(QWidget* parent) : QDialog(parent), ui(new Ui::IconPickerDialog) +{ + ui->setupUi(this); + setWindowModality(Qt::WindowModal); + + searchBar = new QLineEdit(this); + searchBar->setPlaceholderText(tr("Search...")); + ui->verticalLayout->insertWidget(0, searchBar); + + proxyModel = new QSortFilterProxyModel(this); + proxyModel->setSourceModel(APPLICATION->icons().get()); + proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + ui->iconView->setModel(proxyModel); + + auto contentsWidget = ui->iconView; + contentsWidget->setViewMode(QListView::IconMode); + contentsWidget->setFlow(QListView::LeftToRight); + contentsWidget->setIconSize(QSize(48, 48)); + contentsWidget->setMovement(QListView::Static); + contentsWidget->setResizeMode(QListView::Adjust); + contentsWidget->setSelectionMode(QAbstractItemView::SingleSelection); + contentsWidget->setSpacing(5); + contentsWidget->setWordWrap(false); + contentsWidget->setWrapping(true); + contentsWidget->setUniformItemSizes(true); + contentsWidget->setTextElideMode(Qt::ElideRight); + contentsWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + contentsWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + contentsWidget->setItemDelegate(new ListViewDelegate(contentsWidget)); + + // contentsWidget->setAcceptDrops(true); + contentsWidget->setDropIndicatorShown(true); + contentsWidget->viewport()->setAcceptDrops(true); + contentsWidget->setDragDropMode(QAbstractItemView::DropOnly); + contentsWidget->setDefaultDropAction(Qt::CopyAction); + + contentsWidget->installEventFilter(this); + + contentsWidget->setModel(proxyModel); + + // NOTE: ResetRole forces the button to be on the left, while the OK/Cancel ones are on the right. We win. + auto buttonAdd = ui->buttonBox->addButton(tr("Add Icon"), QDialogButtonBox::ResetRole); + buttonRemove = ui->buttonBox->addButton(tr("Remove Icon"), QDialogButtonBox::ResetRole); + + ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); + ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK")); + + connect(buttonAdd, &QPushButton::clicked, this, &IconPickerDialog::addNewIcon); + connect(buttonRemove, &QPushButton::clicked, this, &IconPickerDialog::removeSelectedIcon); + + connect(contentsWidget, &QAbstractItemView::doubleClicked, this, &IconPickerDialog::activated); + + connect(contentsWidget->selectionModel(), + &QItemSelectionModel::selectionChanged, + this, + &IconPickerDialog::selectionChanged); + + auto buttonFolder = ui->buttonBox->addButton(tr("Open Folder"), QDialogButtonBox::ResetRole); + connect(buttonFolder, &QPushButton::clicked, this, &IconPickerDialog::openFolder); + connect(searchBar, &QLineEdit::textChanged, this, &IconPickerDialog::filterIcons); + // Prevent incorrect indices from e.g. filesystem changes + connect(APPLICATION->icons().get(), + &projt::icons::IconList::iconUpdated, + this, + [this]() { proxyModel->invalidate(); }); +} + +bool IconPickerDialog::eventFilter(QObject* obj, QEvent* evt) +{ + if (obj != ui->iconView) + return QDialog::eventFilter(obj, evt); + if (evt->type() != QEvent::KeyPress) + { + return QDialog::eventFilter(obj, evt); + } + QKeyEvent* keyEvent = static_cast<QKeyEvent*>(evt); + switch (keyEvent->key()) + { + case Qt::Key_Delete: removeSelectedIcon(); return true; + case Qt::Key_Plus: addNewIcon(); return true; + default: break; + } + return QDialog::eventFilter(obj, evt); +} + +void IconPickerDialog::addNewIcon() +{ + //: The title of the select icons open file dialog + QString selectIcons = tr("Select Icons"); + //: The type of icon files + auto filter = projt::icons::getIconFilter(); + QStringList fileNames = QFileDialog::getOpenFileNames(this, selectIcons, QString(), tr("Icons %1").arg(filter)); + APPLICATION->icons()->installIcons(fileNames); +} + +void IconPickerDialog::removeSelectedIcon() +{ + if (APPLICATION->icons()->trashIcon(selectedIconKey)) + return; + + APPLICATION->icons()->deleteIcon(selectedIconKey); +} + +void IconPickerDialog::activated(QModelIndex index) +{ + selectedIconKey = index.data(Qt::UserRole).toString(); + accept(); +} + +void IconPickerDialog::selectionChanged(QItemSelection selected, QItemSelection deselected) +{ + if (selected.empty()) + return; + + QString key = selected.first().indexes().first().data(Qt::UserRole).toString(); + if (!key.isEmpty()) + { + selectedIconKey = key; + } + buttonRemove->setEnabled(APPLICATION->icons()->iconFileExists(selectedIconKey)); +} + +int IconPickerDialog::execWithSelection(QString selection) +{ + auto list = APPLICATION->icons(); + auto contentsWidget = ui->iconView; + selectedIconKey = selection; + + int index_nr = list->getIconIndex(selection); + auto model_index = list->index(index_nr); + auto proxyIndex = proxyModel->mapFromSource(model_index); + + if (proxyIndex.isValid()) + { + contentsWidget->selectionModel()->select(proxyIndex, + QItemSelectionModel::Current | QItemSelectionModel::Select); + QMetaObject::invokeMethod(this, "delayed_scroll", Qt::QueuedConnection, Q_ARG(QModelIndex, proxyIndex)); + } + return QDialog::exec(); +} + +void IconPickerDialog::delayed_scroll(QModelIndex proxy_index) +{ + if (proxy_index.isValid()) + { + ui->iconView->scrollTo(proxy_index); + } +} + +IconPickerDialog::~IconPickerDialog() +{ + delete ui; +} + +void IconPickerDialog::openFolder() +{ + DesktopServices::openPath(APPLICATION->icons()->iconDirectory(selectedIconKey), true); +} + +void IconPickerDialog::filterIcons(const QString& query) +{ + proxyModel->setFilterFixedString(query); +} |
