diff --git a/application/application.pro b/application/application.pro index 924ee025..810d07b0 100644 --- a/application/application.pro +++ b/application/application.pro @@ -97,8 +97,12 @@ HEADERS += sources/about_widget.h SOURCES += sources/about_widget.cc HEADERS += sources/bone_list_model.h SOURCES += sources/bone_list_model.cc +HEADERS += sources/bone_manage_widget.h +SOURCES += sources/bone_manage_widget.cc HEADERS += sources/bone_preview_grid_widget.h SOURCES += sources/bone_preview_grid_widget.cc +HEADERS += sources/bone_property_widget.h +SOURCES += sources/bone_property_widget.cc HEADERS += sources/ccd_ik_resolver.h SOURCES += sources/ccd_ik_resolver.cc HEADERS += sources/component_list_model.h diff --git a/application/sources/bone_manage_widget.cc b/application/sources/bone_manage_widget.cc new file mode 100644 index 00000000..62973fbf --- /dev/null +++ b/application/sources/bone_manage_widget.cc @@ -0,0 +1,117 @@ +#include "bone_manage_widget.h" +#include "bone_list_model.h" +#include "bone_preview_grid_widget.h" +#include "bone_property_widget.h" +#include "document.h" +#include "theme.h" +#include +#include +#include + +BoneManageWidget::BoneManageWidget(Document* document, QWidget* parent) + : QWidget(parent) + , m_document(document) +{ + setContextMenuPolicy(Qt::CustomContextMenu); + + QHBoxLayout* toolsLayout = new QHBoxLayout; + toolsLayout->setSpacing(0); + toolsLayout->setMargin(0); + + setStyleSheet("QPushButton:disabled {border: 0; color: " + Theme::gray.name() + "}"); + + auto createButton = [](QChar icon, const QString& title) { + QPushButton* button = new QPushButton(icon); + Theme::initIconButton(button); + button->setToolTip(title); + return button; + }; + + m_selectButton = createButton(QChar(fa::objectgroup), tr("Select them on canvas")); + m_propertyButton = createButton(QChar(fa::sliders), tr("Configure properties")); + + toolsLayout->addWidget(m_selectButton); + toolsLayout->addWidget(m_propertyButton); + toolsLayout->addStretch(); + + QWidget* toolsWidget = new QWidget(); + toolsWidget->setObjectName("tools"); + toolsWidget->setStyleSheet("QWidget#tools {background: qlineargradient( x1:0 y1:0, x2:1 y2:0, stop:0 transparent, stop:0.5 " + Theme::black.name() + ", stop:1 transparent)};"); + toolsWidget->setLayout(toolsLayout); + + m_bonePreviewGridWidget = new BonePreviewGridWidget(document); + + connect(m_bonePreviewGridWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &BoneManageWidget::updateToolButtons); + connect(m_bonePreviewGridWidget, &BonePreviewGridWidget::unselectAllOnCanvas, this, &BoneManageWidget::unselectAllOnCanvas); + connect(m_bonePreviewGridWidget, &BonePreviewGridWidget::selectNodeOnCanvas, this, &BoneManageWidget::selectNodeOnCanvas); + + connect(m_propertyButton, &QPushButton::clicked, this, &BoneManageWidget::showSelectedBoneProperties); + + connect(this, &BoneManageWidget::groupOperationAdded, m_document, &Document::saveSnapshot); + + connect(this, &BoneManageWidget::customContextMenuRequested, this, &BoneManageWidget::showContextMenu); + + QVBoxLayout* mainLayout = new QVBoxLayout; + mainLayout->addWidget(toolsWidget); + mainLayout->addWidget(m_bonePreviewGridWidget); + + setLayout(mainLayout); + + updateToolButtons(); +} + +void BoneManageWidget::showSelectedBoneProperties() +{ + auto boneIds = m_bonePreviewGridWidget->getSelectedBoneIds(); + if (boneIds.empty()) + return; + + auto* propertyWidget = new BonePropertyWidget(m_document, boneIds); + + auto menu = std::make_unique(this->parentWidget()); + QWidgetAction* widgetAction = new QWidgetAction(menu.get()); + widgetAction->setDefaultWidget(propertyWidget); + menu->addAction(widgetAction); + + auto x = mapToGlobal(QPoint(0, 0)).x(); + if (x <= 0) + x = QCursor::pos().x(); + menu->exec(QPoint( + x - propertyWidget->width(), + QCursor::pos().y())); +} + +void BoneManageWidget::selectBoneByBoneId(const dust3d::Uuid& boneId) +{ + QModelIndex index = m_bonePreviewGridWidget->boneListModel()->boneIdToIndex(boneId); + if (index.isValid()) { + m_bonePreviewGridWidget->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); + m_bonePreviewGridWidget->scrollTo(index); + return; + } + dust3dDebug << "Unable to select bone:" << boneId.toString(); +} + +void BoneManageWidget::updateToolButtons() +{ + auto selectedBones = m_bonePreviewGridWidget->getSelectedBones(); + bool enableSelectButton = false; + bool enablePropertyButton = false; + for (const auto& bone : selectedBones) { + enablePropertyButton = true; + enableSelectButton = true; + } + m_selectButton->setEnabled(enableSelectButton); + m_propertyButton->setEnabled(enablePropertyButton); +} + +void BoneManageWidget::showContextMenu(const QPoint& pos) +{ + auto selectedBoneIds = m_bonePreviewGridWidget->getSelectedBoneIds(); + if (selectedBoneIds.empty()) + return; + + QMenu contextMenu(this); + + contextMenu.exec(mapToGlobal(pos)); +} diff --git a/application/sources/bone_manage_widget.h b/application/sources/bone_manage_widget.h new file mode 100644 index 00000000..f87a4446 --- /dev/null +++ b/application/sources/bone_manage_widget.h @@ -0,0 +1,34 @@ +#ifndef DUST3D_APPLICATION_BONE_MANAGE_WIDGET_H_ +#define DUST3D_APPLICATION_BONE_MANAGE_WIDGET_H_ + +#include +#include + +class Document; +class BonePreviewGridWidget; +class BonePropertyWidget; +class QPushButton; + +class BoneManageWidget : public QWidget { + Q_OBJECT +signals: + void unselectAllOnCanvas(); + void selectNodeOnCanvas(const dust3d::Uuid& boneId); + void groupOperationAdded(); +public slots: + void selectBoneByBoneId(const dust3d::Uuid& boneId); + void showSelectedBoneProperties(); + void showContextMenu(const QPoint& pos); + +public: + BoneManageWidget(Document* document, QWidget* parent = nullptr); + +private: + Document* m_document = nullptr; + BonePreviewGridWidget* m_bonePreviewGridWidget = nullptr; + QPushButton* m_selectButton = nullptr; + QPushButton* m_propertyButton = nullptr; + void updateToolButtons(); +}; + +#endif diff --git a/application/sources/bone_property_widget.cc b/application/sources/bone_property_widget.cc new file mode 100644 index 00000000..63900191 --- /dev/null +++ b/application/sources/bone_property_widget.cc @@ -0,0 +1,38 @@ +#include "bone_property_widget.h" +#include "float_number_widget.h" +#include "image_preview_widget.h" +#include "theme.h" +#include +#include +#include +#include +#include + +BonePropertyWidget::BonePropertyWidget(Document* document, + const std::vector& boneIds, + QWidget* parent) + : QWidget(parent) + , m_document(document) + , m_boneIds(boneIds) +{ + prepareBoneIds(); + + QVBoxLayout* mainLayout = new QVBoxLayout; + + mainLayout->setSizeConstraint(QLayout::SetFixedSize); + + connect(this, &BonePropertyWidget::groupOperationAdded, m_document, &Document::saveSnapshot); + + setLayout(mainLayout); + + setFixedSize(minimumSizeHint()); +} + +void BonePropertyWidget::prepareBoneIds() +{ + if (1 == m_boneIds.size()) { + m_bone = m_document->findBone(m_boneIds.front()); + if (nullptr != m_bone) + m_boneId = m_boneIds.front(); + } +} diff --git a/application/sources/bone_property_widget.h b/application/sources/bone_property_widget.h new file mode 100644 index 00000000..d83d92b1 --- /dev/null +++ b/application/sources/bone_property_widget.h @@ -0,0 +1,27 @@ +#ifndef DUST3D_APPLICATION_BONE_PROPERTY_WIDGET_H_ +#define DUST3D_APPLICATION_BONE_PROPERTY_WIDGET_H_ + +#include "document.h" +#include +#include + +class BonePropertyWidget : public QWidget { + Q_OBJECT +signals: + void groupOperationAdded(); + +public: + BonePropertyWidget(Document* document, + const std::vector& boneIds, + QWidget* parent = nullptr); +public slots: + +private: + Document* m_document = nullptr; + std::vector m_boneIds; + dust3d::Uuid m_boneId; + const Document::Bone* m_bone = nullptr; + void prepareBoneIds(); +}; + +#endif diff --git a/application/sources/document_window.cc b/application/sources/document_window.cc index c736f8b5..34de9193 100644 --- a/application/sources/document_window.cc +++ b/application/sources/document_window.cc @@ -1,5 +1,6 @@ #include "document_window.h" #include "about_widget.h" +#include "bone_manage_widget.h" #include "cut_face_preview.h" #include "document.h" #include "document_saver.h" @@ -269,10 +270,12 @@ DocumentWindow::DocumentWindow() partsDocker->setWidget(m_partManageWidget); addDockWidget(Qt::RightDockWidgetArea, partsDocker); - //QDockWidget* motionsDocker = new QDockWidget(tr("Motions"), this); - //motionsDocker->setAllowedAreas(Qt::RightDockWidgetArea); + QDockWidget* bonesDocker = new QDockWidget(tr("Bones"), this); + m_boneManageWidget = new BoneManageWidget(m_document); + bonesDocker->setWidget(m_boneManageWidget); + bonesDocker->setAllowedAreas(Qt::RightDockWidgetArea); - //tabifyDockWidget(partsDocker, motionsDocker); + tabifyDockWidget(partsDocker, bonesDocker); partsDocker->raise(); diff --git a/application/sources/document_window.h b/application/sources/document_window.h index dcbdc273..469de06e 100644 --- a/application/sources/document_window.h +++ b/application/sources/document_window.h @@ -20,6 +20,7 @@ class Document; class SkeletonGraphicsWidget; class PartManageWidget; +class BoneManageWidget; class ToolbarButton; class SpinnableToolbarIcon; @@ -170,6 +171,7 @@ private: bool m_isComponentPreviewImageDecorationsObsolete = false; PartManageWidget* m_partManageWidget = nullptr; + BoneManageWidget* m_boneManageWidget = nullptr; SpinnableToolbarIcon* m_inprogressIndicator = nullptr;