FreeCAD / src /Mod /Material /Gui /BaseDelegate.cpp
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include <QColorDialog>
#include <QDesktopServices>
#include <QIODevice>
#include <QItemSelectionModel>
#include <QPainter>
#include <QString>
#include <QStringList>
#include <QSvgRenderer>
#include <QTextStream>
#include <QVariant>
#include <limits>
#include <App/Application.h>
#include <Base/Interpreter.h>
#include <Base/Quantity.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/InputField.h>
#include <Gui/PrefWidgets.h>
#include <Gui/SpinBox.h>
#include <Gui/WaitCursor.h>
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/ModelManager.h>
#include "BaseDelegate.h"
#include "ListModel.h"
#include "MaterialSave.h"
using namespace MatGui;
BaseDelegate::BaseDelegate(QObject* parent)
: QStyledItemDelegate(parent)
{}
bool BaseDelegate::newRow(const QAbstractItemModel* model, const QModelIndex& index) const
{
// The model always includes an empty row to allow for additions
return (index.row() == (model->rowCount() - 1));
}
QString BaseDelegate::getStringValue(const QModelIndex& index) const
{
QVariant item = getValue(index);
auto propertyValue = item.value<QString>();
return propertyValue;
}
Color BaseDelegate::parseColor(const QString& color) const
{
QString trimmed = color;
trimmed.replace(QRegularExpression(QStringLiteral("\\(([^<]*)\\)")),
QStringLiteral("\\1"));
QStringList parts = trimmed.split(QStringLiteral(","));
if (parts.length() < 3) {
return Color();
}
return Color(parts.at(0).toDouble(), parts.at(1).toDouble(), parts.at(2).toDouble(),
parts.length() > 3 ? parts.at(3).toDouble() : 1.0);
}
void BaseDelegate::paintQuantity(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
auto model = index.model();
painter->save();
if (newRow(model, index)) {
painter->drawText(option.rect, 0, QString());
}
else {
QString text;
QVariant item = getValue(index);
auto quantity = item.value<Base::Quantity>();
if (quantity.isValid()) {
text = QString::fromStdString(quantity.getUserString());
}
painter->drawText(option.rect, 0, text);
}
painter->restore();
}
void BaseDelegate::paintImage(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
auto propertyValue = getStringValue(index);
painter->save();
QImage img;
if (!propertyValue.isEmpty()) {
QByteArray by = QByteArray::fromBase64(propertyValue.toUtf8());
img = QImage::fromData(by).scaled(64, 64, Qt::KeepAspectRatio);
}
QRect target(option.rect);
if (target.width() > target.height()) {
target.setWidth(target.height());
}
else {
target.setHeight(target.width());
}
painter->drawImage(target, img, img.rect());
painter->restore();
}
void BaseDelegate::paintSVG(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
auto propertyValue = getStringValue(index);
painter->save();
if (!propertyValue.isEmpty()) {
QSvgRenderer renderer(propertyValue.toUtf8());
renderer.render(painter, QRectF(option.rect));
}
painter->restore();
}
void BaseDelegate::paintColor(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
auto propertyValue = getStringValue(index);
painter->save();
int left = option.rect.left() + 2;
int width = option.rect.width() - 4;
if (option.rect.width() > 75) {
left += (option.rect.width() - 75) / 2;
width = 71;
}
painter->fillRect(left, option.rect.top() + 2, width, option.rect.height() - 4, QBrush(Qt::black));
left = option.rect.left() + 5;
width = option.rect.width() - 10;
if (option.rect.width() > 75) {
left += (option.rect.width() - 75) / 2;
width = 65;
}
auto color = parseColor(propertyValue).asValue<QColor>();
painter->fillRect(left, option.rect.top() + 5, width, option.rect.height() - 10, QBrush(color));
painter->restore();
}
void BaseDelegate::paintList(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
Q_UNUSED(index)
painter->save();
QImage list(QStringLiteral(":/icons/list.svg"));
QRect target(option.rect);
if (target.width() > target.height()) {
target.setWidth(target.height());
}
else {
target.setHeight(target.width());
}
painter->drawImage(target, list, list.rect());
painter->restore();
}
void BaseDelegate::paintMultiLineString(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
Q_UNUSED(index)
painter->save();
QImage table(QStringLiteral(":/icons/multiline.svg"));
QRect target(option.rect);
if (target.width() > target.height()) {
target.setWidth(target.height());
}
else {
target.setHeight(target.width());
}
painter->drawImage(target, table, table.rect());
painter->restore();
}
void BaseDelegate::paintArray(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
Q_UNUSED(index)
painter->save();
QImage table(QStringLiteral(":/icons/table.svg"));
QRect target(option.rect);
if (target.width() > target.height()) {
target.setWidth(target.height());
}
else {
target.setHeight(target.width());
}
painter->drawImage(target, table, table.rect());
painter->restore();
}
void BaseDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
auto type = getType(index);
if (type == Materials::MaterialValue::Quantity) {
paintQuantity(painter, option, index);
return;
}
if (type == Materials::MaterialValue::Image) {
paintImage(painter, option, index);
return;
}
if (type == Materials::MaterialValue::SVG) {
paintSVG(painter, option, index);
return;
}
if (type == Materials::MaterialValue::Color) {
paintColor(painter, option, index);
return;
}
if (type == Materials::MaterialValue::List || type == Materials::MaterialValue::FileList
|| type == Materials::MaterialValue::ImageList) {
paintList(painter, option, index);
return;
}
if (type == Materials::MaterialValue::MultiLineString) {
paintMultiLineString(painter, option, index);
return;
}
if (type == Materials::MaterialValue::Array2D || type == Materials::MaterialValue::Array3D) {
paintArray(painter, option, index);
return;
}
QStyledItemDelegate::paint(painter, option, index);
}
QSize BaseDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
Q_UNUSED(option)
Q_UNUSED(index)
auto type = getType(index);
if (type == Materials::MaterialValue::Color) {
return {75, 23}; // Standard QPushButton size
}
if (type == Materials::MaterialValue::Image || type == Materials::MaterialValue::SVG) {
return {64, 64};
}
if (type == Materials::MaterialValue::Array2D || type == Materials::MaterialValue::Array3D
|| type == Materials::MaterialValue::MultiLineString
|| type == Materials::MaterialValue::List || type == Materials::MaterialValue::FileList
|| type == Materials::MaterialValue::ImageList) {
return {23, 23};
}
return QStyledItemDelegate::sizeHint(option, index);
}
void BaseDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
if (!editor) {
return;
}
auto item = getValue(index);
auto type = getType(index);
if (type == Materials::MaterialValue::List) {
auto input = dynamic_cast<Gui::PrefLineEdit*>(editor);
item = input->text();
return;
}
if (type == Materials::MaterialValue::FileList || type == Materials::MaterialValue::File) {
auto chooser = dynamic_cast<Gui::FileChooser*>(editor);
chooser->setFileName(item.toString());
return;
}
if (type == Materials::MaterialValue::Quantity) {
auto input = dynamic_cast<Gui::QuantitySpinBox*>(editor);
// input->setQuantityString(item.toString());
input->setValue(item.value<Base::Quantity>());
return;
}
if (type == Materials::MaterialValue::List || type == Materials::MaterialValue::ImageList) {
// Handled by dialogs
return;
}
QStyledItemDelegate::setEditorData(editor, index);
}
void BaseDelegate::setModelData(QWidget* editor,
QAbstractItemModel* model,
const QModelIndex& index) const
{
QVariant value;
auto type = getType(index);
if (type == Materials::MaterialValue::FileList || type == Materials::MaterialValue::File) {
auto chooser = dynamic_cast<Gui::FileChooser*>(editor);
value = chooser->fileName();
}
else if (type == Materials::MaterialValue::Quantity) {
auto input = dynamic_cast<Gui::QuantitySpinBox*>(editor);
// value = input->text();
// return;
// auto quantity = Base::Quantity::parse(input->text());
auto quantity = input->value();
value = QVariant::fromValue(quantity);
}
else if (type == Materials::MaterialValue::Integer) {
auto spinner = dynamic_cast<Gui::IntSpinBox*>(editor);
value = spinner->value();
}
else if (type == Materials::MaterialValue::Float) {
auto spinner = dynamic_cast<Gui::DoubleSpinBox*>(editor);
value = spinner->value();
}
else if (type == Materials::MaterialValue::Boolean) {
auto combo = dynamic_cast<Gui::PrefComboBox*>(editor);
value = combo->currentText();
}
else if (type == Materials::MaterialValue::Image || type == Materials::MaterialValue::SVG) {
// Value was already saved to the property
notifyChanged(index.model(), index);
return;
}
else {
auto lineEdit = dynamic_cast<Gui::PrefLineEdit*>(editor);
value = lineEdit->text();
}
setValue(model, index, value);
// Q_EMIT model->dataChanged(index, index);
}
QWidget* BaseDelegate::createEditor(QWidget* parent,
const QStyleOptionViewItem& styleOption,
const QModelIndex& index) const
{
Q_UNUSED(styleOption)
auto model = index.model();
// If this is a new row then it has to be added before the editor is created. Otherwise
// Adding the row while the editor is active can change where the editor is placed.
if (newRow(model, index)) {
const_cast<QAbstractItemModel*>(model)->insertRows(index.row(), 1);
}
auto item = getValue(index);
QWidget* editor = createWidget(parent, item, index);
return editor;
}
QWidget*
BaseDelegate::createWidget(QWidget* parent, const QVariant& item, const QModelIndex& index) const
{
QWidget* widget = nullptr;
auto type = getType(index);
if (type == Materials::MaterialValue::Integer) {
auto spinner = new Gui::UIntSpinBox(parent);
spinner->setMinimum(0);
spinner->setMaximum(std::numeric_limits<unsigned>::max());
spinner->setValue(item.toUInt());
widget = spinner;
}
else if (type == Materials::MaterialValue::Float) {
auto spinner = new Gui::DoubleSpinBox(parent);
// the magnetic permeability is the parameter for which many decimals matter
// the most however, even for this, 6 digits are sufficient
spinner->setDecimals(6);
// for almost all Float parameters of materials a step of 1 would be too large
spinner->setSingleStep(0.1);
spinner->setMinimum(std::numeric_limits<double>::min());
spinner->setMaximum(std::numeric_limits<double>::max());
spinner->setValue(item.toDouble());
widget = spinner;
}
else if (type == Materials::MaterialValue::Boolean) {
auto combo = new Gui::PrefComboBox(parent);
combo->insertItem(0, QStringLiteral(""));
combo->insertItem(1, tr("False"));
combo->insertItem(2, tr("True"));
combo->setCurrentText(item.toString());
widget = combo;
}
else if (type == Materials::MaterialValue::Quantity) {
auto input = new Gui::QuantitySpinBox(parent);
input->setMinimum(std::numeric_limits<double>::min());
input->setMaximum(std::numeric_limits<double>::max());
input->setUnitText(getUnits(index));
input->setValue(item.value<Base::Quantity>());
widget = input;
}
else if (type == Materials::MaterialValue::File) {
auto chooser = new Gui::FileChooser(parent);
if (!item.toString().isEmpty()) {
chooser->setFileName(item.toString());
}
widget = chooser;
}
else if (type == Materials::MaterialValue::FileList) {
auto chooser = new Gui::FileChooser(parent);
auto propertyValue = item.toString();
connect(chooser,
&Gui::FileChooser::fileNameChanged,
[this, chooser, index](const QString&) {
setModelData(chooser, const_cast<QAbstractItemModel*>(index.model()), index);
});
connect(chooser,
&Gui::FileChooser::fileNameSelected,
[this, chooser, index](const QString&) {
setModelData(chooser, const_cast<QAbstractItemModel*>(index.model()), index);
});
widget = chooser;
}
else {
// Default editor
auto lineEdit = new Gui::PrefLineEdit(parent);
lineEdit->setText(item.toString());
widget = lineEdit;
}
return widget;
}
#include "moc_BaseDelegate.cpp"