2018-09-18 03:17:35 +00:00
|
|
|
#include <QVBoxLayout>
|
2018-09-21 07:10:18 +00:00
|
|
|
#include <QHBoxLayout>
|
2018-09-18 03:17:35 +00:00
|
|
|
#include <QFormLayout>
|
|
|
|
#include <QGridLayout>
|
|
|
|
#include <QMenu>
|
|
|
|
#include <QWidgetAction>
|
2018-09-21 07:10:18 +00:00
|
|
|
#include <QLineEdit>
|
|
|
|
#include <QMessageBox>
|
2018-11-05 15:47:21 +00:00
|
|
|
#include <QFileDialog>
|
2018-11-09 03:20:48 +00:00
|
|
|
#include <QSpinBox>
|
2019-06-16 08:45:03 +00:00
|
|
|
#include <QSlider>
|
2018-09-18 03:17:35 +00:00
|
|
|
#include "theme.h"
|
2018-09-21 07:10:18 +00:00
|
|
|
#include "poseeditwidget.h"
|
2018-09-18 03:17:35 +00:00
|
|
|
#include "floatnumberwidget.h"
|
2018-09-21 07:10:18 +00:00
|
|
|
#include "version.h"
|
2018-11-01 15:24:06 +00:00
|
|
|
#include "poserconstruct.h"
|
2018-11-05 15:47:21 +00:00
|
|
|
#include "graphicscontainerwidget.h"
|
|
|
|
#include "documentwindow.h"
|
|
|
|
#include "shortcuts.h"
|
|
|
|
#include "imageforever.h"
|
2018-09-18 03:17:35 +00:00
|
|
|
|
2019-06-16 08:45:03 +00:00
|
|
|
float PoseEditWidget::m_defaultBlur = 0.5;
|
|
|
|
|
2018-10-25 00:19:38 +00:00
|
|
|
PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) :
|
2018-09-21 07:10:18 +00:00
|
|
|
QDialog(parent),
|
2018-11-05 15:47:21 +00:00
|
|
|
m_document(document),
|
|
|
|
m_poseDocument(new PoseDocument)
|
2018-09-18 03:17:35 +00:00
|
|
|
{
|
|
|
|
m_posePreviewManager = new PosePreviewManager();
|
|
|
|
connect(m_posePreviewManager, &PosePreviewManager::renderDone, [=]() {
|
2018-09-21 07:10:18 +00:00
|
|
|
if (m_closed) {
|
|
|
|
close();
|
|
|
|
return;
|
|
|
|
}
|
2018-09-18 03:17:35 +00:00
|
|
|
if (m_isPreviewDirty)
|
|
|
|
updatePreview();
|
|
|
|
});
|
2018-09-21 07:10:18 +00:00
|
|
|
connect(m_posePreviewManager, &PosePreviewManager::resultPreviewMeshChanged, [=]() {
|
|
|
|
m_previewWidget->updateMesh(m_posePreviewManager->takeResultPreviewMesh());
|
|
|
|
});
|
2018-09-18 03:17:35 +00:00
|
|
|
|
2018-11-05 15:47:21 +00:00
|
|
|
SkeletonGraphicsWidget *graphicsWidget = new SkeletonGraphicsWidget(m_poseDocument);
|
|
|
|
graphicsWidget->setNodePositionModifyOnly(true);
|
2019-06-16 08:45:03 +00:00
|
|
|
graphicsWidget->setBackgroundBlur(m_defaultBlur);
|
2018-11-05 15:47:21 +00:00
|
|
|
m_poseGraphicsWidget = graphicsWidget;
|
|
|
|
|
|
|
|
initShortCuts(this, graphicsWidget);
|
|
|
|
|
|
|
|
GraphicsContainerWidget *containerWidget = new GraphicsContainerWidget;
|
|
|
|
containerWidget->setGraphicsWidget(graphicsWidget);
|
|
|
|
QGridLayout *containerLayout = new QGridLayout;
|
|
|
|
containerLayout->setSpacing(0);
|
|
|
|
containerLayout->setContentsMargins(1, 0, 0, 0);
|
|
|
|
containerLayout->addWidget(graphicsWidget);
|
|
|
|
containerWidget->setLayout(containerLayout);
|
|
|
|
containerWidget->setMinimumSize(400, 400);
|
|
|
|
|
2019-06-16 08:45:03 +00:00
|
|
|
m_previewWidget = new ModelWidget(this);
|
|
|
|
m_previewWidget->setFixedSize(384, 384);
|
|
|
|
m_previewWidget->enableMove(true);
|
|
|
|
m_previewWidget->enableZoom(false);
|
|
|
|
m_previewWidget->move(-64, 0);
|
2018-11-05 15:47:21 +00:00
|
|
|
|
2019-06-17 22:46:59 +00:00
|
|
|
m_poseGraphicsWidget->setModelWidget(m_previewWidget);
|
|
|
|
containerWidget->setModelWidget(m_previewWidget);
|
|
|
|
|
2018-11-05 15:47:21 +00:00
|
|
|
connect(containerWidget, &GraphicsContainerWidget::containerSizeChanged,
|
|
|
|
graphicsWidget, &SkeletonGraphicsWidget::canvasResized);
|
|
|
|
|
|
|
|
connect(graphicsWidget, &SkeletonGraphicsWidget::moveNodeBy, m_poseDocument, &PoseDocument::moveNodeBy);
|
|
|
|
connect(graphicsWidget, &SkeletonGraphicsWidget::setNodeOrigin, m_poseDocument, &PoseDocument::setNodeOrigin);
|
|
|
|
connect(graphicsWidget, &SkeletonGraphicsWidget::groupOperationAdded, m_poseDocument, &PoseDocument::saveHistoryItem);
|
|
|
|
connect(graphicsWidget, &SkeletonGraphicsWidget::undo, m_poseDocument, &PoseDocument::undo);
|
|
|
|
connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_poseDocument, &PoseDocument::redo);
|
2018-11-10 00:21:07 +00:00
|
|
|
connect(graphicsWidget, &SkeletonGraphicsWidget::paste, m_poseDocument, &PoseDocument::paste);
|
2018-11-09 23:56:58 +00:00
|
|
|
connect(graphicsWidget, &SkeletonGraphicsWidget::switchChainSide, m_poseDocument, &PoseDocument::switchChainSide);
|
2018-11-05 15:47:21 +00:00
|
|
|
|
|
|
|
connect(m_poseDocument, &PoseDocument::cleanup, graphicsWidget, &SkeletonGraphicsWidget::removeAllContent);
|
|
|
|
|
|
|
|
connect(m_poseDocument, &PoseDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded);
|
|
|
|
connect(m_poseDocument, &PoseDocument::edgeAdded, graphicsWidget, &SkeletonGraphicsWidget::edgeAdded);
|
|
|
|
connect(m_poseDocument, &PoseDocument::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged);
|
2019-06-17 12:49:08 +00:00
|
|
|
connect(m_poseDocument, &PoseDocument::partVisibleStateChanged, graphicsWidget, &SkeletonGraphicsWidget::partVisibleStateChanged);
|
2018-11-05 15:47:21 +00:00
|
|
|
|
|
|
|
connect(m_poseDocument, &PoseDocument::parametersChanged, this, [&]() {
|
2018-11-09 03:20:48 +00:00
|
|
|
m_currentParameters.clear();
|
|
|
|
m_poseDocument->toParameters(m_currentParameters);
|
|
|
|
syncFrameFromCurrent();
|
2018-11-05 15:47:21 +00:00
|
|
|
emit parametersAdjusted();
|
|
|
|
});
|
2018-09-21 07:10:18 +00:00
|
|
|
|
2019-06-16 08:45:03 +00:00
|
|
|
QSlider *opacitySlider = new QSlider(Qt::Horizontal);
|
|
|
|
opacitySlider->setFixedWidth(100);
|
|
|
|
opacitySlider->setMaximum(10);
|
|
|
|
opacitySlider->setMinimum(0);
|
|
|
|
opacitySlider->setValue(m_defaultBlur * 10);
|
|
|
|
|
|
|
|
connect(opacitySlider, &QSlider::valueChanged, this, [=](int value) {
|
|
|
|
graphicsWidget->setBackgroundBlur((float)value / 10);
|
|
|
|
});
|
2019-06-17 12:49:08 +00:00
|
|
|
|
|
|
|
auto updateSideVisibleButtonState = [=](QPushButton *button, SkeletonSide side) {
|
|
|
|
this->updateSideButtonState(button, m_poseDocument->isSideVisible(side));
|
|
|
|
};
|
|
|
|
|
|
|
|
QPushButton *leftSideVisibleButton = new QPushButton(QChar('L'));
|
|
|
|
leftSideVisibleButton->setToolTip(tr("Toggle Left Side Visibility"));
|
|
|
|
initSideButton(leftSideVisibleButton);
|
|
|
|
updateSideVisibleButtonState(leftSideVisibleButton, SkeletonSide::Left);
|
|
|
|
connect(leftSideVisibleButton, &QPushButton::clicked, this, [=]() {
|
|
|
|
m_poseDocument->setSideVisiableState(SkeletonSide::Left, !m_poseDocument->isSideVisible(SkeletonSide::Left));
|
|
|
|
});
|
|
|
|
|
|
|
|
QPushButton *middleSideVisibleButton = new QPushButton(QChar('M'));
|
|
|
|
middleSideVisibleButton->setToolTip(tr("Toggle Middle Side Visibility"));
|
|
|
|
initSideButton(middleSideVisibleButton);
|
|
|
|
updateSideVisibleButtonState(middleSideVisibleButton, SkeletonSide::None);
|
|
|
|
connect(middleSideVisibleButton, &QPushButton::clicked, this, [=]() {
|
|
|
|
m_poseDocument->setSideVisiableState(SkeletonSide::None, !m_poseDocument->isSideVisible(SkeletonSide::None));
|
|
|
|
});
|
|
|
|
|
|
|
|
QPushButton *rightSideVisibleButton = new QPushButton(QChar('R'));
|
|
|
|
rightSideVisibleButton->setToolTip(tr("Toggle Right Side Visibility"));
|
|
|
|
initSideButton(rightSideVisibleButton);
|
|
|
|
updateSideVisibleButtonState(rightSideVisibleButton, SkeletonSide::Right);
|
|
|
|
connect(rightSideVisibleButton, &QPushButton::clicked, this, [=]() {
|
|
|
|
m_poseDocument->setSideVisiableState(SkeletonSide::Right, !m_poseDocument->isSideVisible(SkeletonSide::Right));
|
|
|
|
});
|
|
|
|
|
|
|
|
connect(m_poseDocument, &PoseDocument::sideVisibleStateChanged, this, [=](SkeletonSide side) {
|
|
|
|
switch (side) {
|
|
|
|
case SkeletonSide::Left:
|
|
|
|
updateSideVisibleButtonState(leftSideVisibleButton, side);
|
|
|
|
break;
|
|
|
|
case SkeletonSide::None:
|
|
|
|
updateSideVisibleButtonState(middleSideVisibleButton, side);
|
|
|
|
break;
|
|
|
|
case SkeletonSide::Right:
|
|
|
|
updateSideVisibleButtonState(rightSideVisibleButton, side);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
QHBoxLayout *sideButtonLayout = new QHBoxLayout;
|
|
|
|
sideButtonLayout->setSpacing(0);
|
|
|
|
sideButtonLayout->addStretch();
|
|
|
|
sideButtonLayout->addWidget(rightSideVisibleButton);
|
2019-06-17 22:46:59 +00:00
|
|
|
sideButtonLayout->addWidget(middleSideVisibleButton);
|
|
|
|
sideButtonLayout->addWidget(leftSideVisibleButton);
|
2019-06-17 12:49:08 +00:00
|
|
|
sideButtonLayout->addStretch();
|
2019-06-16 08:45:03 +00:00
|
|
|
|
|
|
|
QHBoxLayout *sliderLayout = new QHBoxLayout;
|
|
|
|
sliderLayout->addStretch();
|
|
|
|
sliderLayout->addSpacing(50);
|
|
|
|
sliderLayout->addWidget(new QLabel(tr("Dark")));
|
|
|
|
sliderLayout->addWidget(opacitySlider);
|
|
|
|
sliderLayout->addWidget(new QLabel(tr("Bright")));
|
|
|
|
sliderLayout->addSpacing(50);
|
|
|
|
sliderLayout->addStretch();
|
|
|
|
|
|
|
|
QVBoxLayout *previewLayout = new QVBoxLayout;
|
|
|
|
previewLayout->addStretch();
|
2019-06-17 12:49:08 +00:00
|
|
|
previewLayout->addLayout(sideButtonLayout);
|
2019-06-16 08:45:03 +00:00
|
|
|
previewLayout->addLayout(sliderLayout);
|
|
|
|
previewLayout->addSpacing(20);
|
|
|
|
|
2018-09-21 07:10:18 +00:00
|
|
|
QHBoxLayout *paramtersLayout = new QHBoxLayout;
|
2018-11-05 15:47:21 +00:00
|
|
|
paramtersLayout->addWidget(containerWidget);
|
2018-09-21 07:10:18 +00:00
|
|
|
|
2019-06-16 08:45:03 +00:00
|
|
|
QHBoxLayout *topLayout = new QHBoxLayout;
|
|
|
|
topLayout->addLayout(previewLayout);
|
|
|
|
topLayout->addWidget(Theme::createVerticalLineWidget());
|
|
|
|
topLayout->addLayout(paramtersLayout);
|
|
|
|
topLayout->setStretch(2, 1);
|
|
|
|
|
2018-09-21 07:10:18 +00:00
|
|
|
m_nameEdit = new QLineEdit;
|
2018-10-23 14:57:47 +00:00
|
|
|
m_nameEdit->setFixedWidth(200);
|
2018-09-21 07:10:18 +00:00
|
|
|
connect(m_nameEdit, &QLineEdit::textChanged, this, [=]() {
|
2018-11-09 03:20:48 +00:00
|
|
|
setUnsaveState();
|
2018-09-21 07:10:18 +00:00
|
|
|
});
|
2019-03-03 00:05:13 +00:00
|
|
|
|
|
|
|
m_durationEdit = new QDoubleSpinBox();
|
|
|
|
m_durationEdit->setDecimals(2);
|
|
|
|
m_durationEdit->setMaximum(60);
|
|
|
|
m_durationEdit->setMinimum(0);
|
|
|
|
m_durationEdit->setSingleStep(0.1);
|
|
|
|
m_durationEdit->setValue(m_duration);
|
|
|
|
|
|
|
|
connect(m_durationEdit, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, [=](double value) {
|
|
|
|
setDuration((float)value);
|
|
|
|
});
|
|
|
|
|
2018-11-09 15:07:26 +00:00
|
|
|
QPushButton *saveButton = new QPushButton(tr("Save"));
|
2018-09-21 07:10:18 +00:00
|
|
|
connect(saveButton, &QPushButton::clicked, this, &PoseEditWidget::save);
|
|
|
|
saveButton->setDefault(true);
|
|
|
|
|
2018-11-05 15:47:21 +00:00
|
|
|
QPushButton *changeReferenceSheet = new QPushButton(tr("Change Reference Sheet..."));
|
|
|
|
connect(changeReferenceSheet, &QPushButton::clicked, this, &PoseEditWidget::changeTurnaround);
|
|
|
|
connect(m_poseDocument, &PoseDocument::turnaroundChanged,
|
|
|
|
graphicsWidget, &SkeletonGraphicsWidget::turnaroundChanged);
|
|
|
|
|
2018-11-09 03:20:48 +00:00
|
|
|
m_framesSettingButton = new QPushButton();
|
|
|
|
connect(m_framesSettingButton, &QPushButton::clicked, this, [=]() {
|
|
|
|
showFramesSettingPopup(mapFromGlobal(QCursor::pos()));
|
|
|
|
});
|
|
|
|
|
|
|
|
m_currentFrameSlider = new QSlider(Qt::Horizontal);
|
|
|
|
m_currentFrameSlider->setRange(0, m_frames.size() - 1);
|
|
|
|
m_currentFrameSlider->setValue(m_currentFrame);
|
2019-03-03 00:05:13 +00:00
|
|
|
//m_currentFrameSlider->hide();
|
2018-11-09 03:20:48 +00:00
|
|
|
connect(m_currentFrameSlider, static_cast<void (QSlider::*)(int)>(&QSlider::valueChanged), this, [=](int value) {
|
|
|
|
setCurrentFrame(value);
|
|
|
|
});
|
|
|
|
|
2018-11-05 15:47:21 +00:00
|
|
|
connect(m_document, &Document::resultRigChanged, this, &PoseEditWidget::updatePoseDocument);
|
|
|
|
|
2019-03-03 00:05:13 +00:00
|
|
|
QPushButton *moveToFirstFrameButton = new QPushButton(Theme::awesome()->icon(fa::angledoubleleft), "");
|
|
|
|
connect(moveToFirstFrameButton, &QPushButton::clicked, this, [=]() {
|
|
|
|
setCurrentFrame(0);
|
|
|
|
});
|
|
|
|
|
|
|
|
QPushButton *moveToPreviousFrameButton = new QPushButton(Theme::awesome()->icon(fa::angleleft), "");
|
|
|
|
connect(moveToPreviousFrameButton, &QPushButton::clicked, this, [=]() {
|
|
|
|
if (m_currentFrame > 0)
|
|
|
|
setCurrentFrame(m_currentFrame - 1);
|
|
|
|
});
|
|
|
|
|
|
|
|
QPushButton *moveToNextFrameButton = new QPushButton(Theme::awesome()->icon(fa::angleright), "");
|
|
|
|
connect(moveToNextFrameButton, &QPushButton::clicked, this, [=]() {
|
|
|
|
if (m_currentFrame + 1 < (int)m_frames.size())
|
|
|
|
setCurrentFrame(m_currentFrame + 1);
|
|
|
|
});
|
|
|
|
|
|
|
|
QPushButton *moveToLastFrameButton = new QPushButton(Theme::awesome()->icon(fa::angledoubleright), "");
|
|
|
|
connect(moveToLastFrameButton, &QPushButton::clicked, this, [=]() {
|
|
|
|
if (!m_frames.empty())
|
|
|
|
setCurrentFrame(m_frames.size() - 1);
|
|
|
|
});
|
|
|
|
|
|
|
|
QPushButton *insertAfterFrameButton = new QPushButton(Theme::awesome()->icon(fa::plus), "");
|
|
|
|
connect(insertAfterFrameButton, &QPushButton::clicked, this, &PoseEditWidget::insertFrameAfterCurrentFrame);
|
|
|
|
|
|
|
|
QPushButton *deleteFrameButton = new QPushButton(Theme::awesome()->icon(fa::trash), "");
|
|
|
|
connect(deleteFrameButton, &QPushButton::clicked, this, &PoseEditWidget::removeCurrentFrame);
|
|
|
|
|
|
|
|
QHBoxLayout *timelineLayout = new QHBoxLayout;
|
|
|
|
timelineLayout->addWidget(insertAfterFrameButton);
|
|
|
|
timelineLayout->addWidget(moveToFirstFrameButton);
|
|
|
|
timelineLayout->addWidget(moveToPreviousFrameButton);
|
|
|
|
timelineLayout->addWidget(moveToNextFrameButton);
|
|
|
|
timelineLayout->addWidget(moveToLastFrameButton);
|
|
|
|
timelineLayout->addWidget(m_framesSettingButton);
|
|
|
|
timelineLayout->addWidget(m_currentFrameSlider);
|
|
|
|
timelineLayout->addWidget(deleteFrameButton);
|
|
|
|
timelineLayout->setStretch(6, 1);
|
|
|
|
|
2018-09-21 07:10:18 +00:00
|
|
|
QHBoxLayout *baseInfoLayout = new QHBoxLayout;
|
|
|
|
baseInfoLayout->addWidget(new QLabel(tr("Name")));
|
|
|
|
baseInfoLayout->addWidget(m_nameEdit);
|
2019-03-03 00:05:13 +00:00
|
|
|
baseInfoLayout->addSpacing(10);
|
|
|
|
baseInfoLayout->addWidget(new QLabel(tr("Duration")));
|
|
|
|
baseInfoLayout->addWidget(m_durationEdit);
|
2018-09-21 07:10:18 +00:00
|
|
|
baseInfoLayout->addStretch();
|
2019-03-03 00:05:13 +00:00
|
|
|
baseInfoLayout->addWidget(changeReferenceSheet);
|
2018-09-21 07:10:18 +00:00
|
|
|
baseInfoLayout->addWidget(saveButton);
|
|
|
|
|
|
|
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
2019-06-16 08:45:03 +00:00
|
|
|
mainLayout->addLayout(topLayout);
|
2018-09-21 07:10:18 +00:00
|
|
|
mainLayout->addWidget(Theme::createHorizontalLineWidget());
|
2019-03-03 00:05:13 +00:00
|
|
|
mainLayout->addLayout(timelineLayout);
|
2018-09-21 07:10:18 +00:00
|
|
|
mainLayout->addLayout(baseInfoLayout);
|
2018-09-18 03:17:35 +00:00
|
|
|
|
2018-09-21 07:10:18 +00:00
|
|
|
setLayout(mainLayout);
|
2018-09-18 03:17:35 +00:00
|
|
|
|
2018-09-21 07:10:18 +00:00
|
|
|
connect(this, &PoseEditWidget::parametersAdjusted, this, &PoseEditWidget::updatePreview);
|
|
|
|
connect(this, &PoseEditWidget::parametersAdjusted, [=]() {
|
2018-11-09 03:20:48 +00:00
|
|
|
setUnsaveState();
|
2018-09-21 07:10:18 +00:00
|
|
|
});
|
2018-10-25 00:19:38 +00:00
|
|
|
connect(this, &PoseEditWidget::addPose, m_document, &Document::addPose);
|
|
|
|
connect(this, &PoseEditWidget::renamePose, m_document, &Document::renamePose);
|
2018-11-09 03:20:48 +00:00
|
|
|
connect(this, &PoseEditWidget::setPoseFrames, m_document, &Document::setPoseFrames);
|
|
|
|
connect(this, &PoseEditWidget::setPoseTurnaroundImageId, m_document, &Document::setPoseTurnaroundImageId);
|
2018-09-21 07:10:18 +00:00
|
|
|
|
2018-11-05 15:47:21 +00:00
|
|
|
updatePoseDocument();
|
2018-09-21 07:10:18 +00:00
|
|
|
updateTitle();
|
2018-11-09 03:20:48 +00:00
|
|
|
updateFramesSettingButton();
|
2018-11-05 15:47:21 +00:00
|
|
|
m_poseDocument->saveHistoryItem();
|
2018-09-18 03:17:35 +00:00
|
|
|
}
|
|
|
|
|
2019-06-17 12:49:08 +00:00
|
|
|
void PoseEditWidget::initSideButton(QPushButton *button)
|
|
|
|
{
|
|
|
|
QFont font;
|
|
|
|
font.setWeight(QFont::Light);
|
|
|
|
font.setPixelSize(Theme::toolIconFontSize);
|
|
|
|
font.setBold(false);
|
|
|
|
|
|
|
|
button->setFont(font);
|
|
|
|
button->setFixedSize(Theme::toolIconSize, Theme::toolIconSize);
|
|
|
|
button->setStyleSheet("QPushButton {color: #f7d9c8}");
|
|
|
|
button->setFocusPolicy(Qt::NoFocus);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::updateSideButtonState(QPushButton *button, bool visible)
|
|
|
|
{
|
|
|
|
if (visible)
|
|
|
|
button->setStyleSheet("QPushButton {color: #f7d9c8}");
|
|
|
|
else
|
|
|
|
button->setStyleSheet("QPushButton {color: #252525}");
|
|
|
|
}
|
|
|
|
|
2018-11-09 03:20:48 +00:00
|
|
|
void PoseEditWidget::showFramesSettingPopup(const QPoint &pos)
|
|
|
|
{
|
|
|
|
QMenu popupMenu;
|
|
|
|
|
|
|
|
QWidget *popup = new QWidget;
|
|
|
|
|
|
|
|
QSpinBox *framesEdit = new QSpinBox();
|
|
|
|
framesEdit->setMaximum(60);
|
|
|
|
framesEdit->setMinimum(1);
|
|
|
|
framesEdit->setSingleStep(1);
|
|
|
|
framesEdit->setValue(m_frames.size());
|
|
|
|
|
|
|
|
connect(framesEdit, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [=](int value) {
|
|
|
|
setFrameCount(value);
|
|
|
|
});
|
|
|
|
|
|
|
|
QFormLayout *formLayout = new QFormLayout;
|
|
|
|
formLayout->addRow(tr("Frames:"), framesEdit);
|
|
|
|
|
|
|
|
popup->setLayout(formLayout);
|
|
|
|
|
|
|
|
QWidgetAction *action = new QWidgetAction(this);
|
|
|
|
action->setDefaultWidget(popup);
|
|
|
|
|
|
|
|
popupMenu.addAction(action);
|
|
|
|
|
|
|
|
popupMenu.exec(mapToGlobal(pos));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::updateFramesSettingButton()
|
|
|
|
{
|
|
|
|
m_currentFrameSlider->setRange(0, m_frames.size() - 1);
|
2019-03-03 00:05:13 +00:00
|
|
|
if (m_currentFrame != m_currentFrameSlider->value())
|
|
|
|
m_currentFrameSlider->setValue(m_currentFrame);
|
2018-11-09 03:20:48 +00:00
|
|
|
m_framesSettingButton->setText(tr("Frame: %1 / %2").arg(QString::number(m_currentFrame + 1).rightJustified(2, ' ')).arg(QString::number(m_frames.size()).leftJustified(2, ' ')));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::ensureEnoughFrames()
|
|
|
|
{
|
|
|
|
if (m_currentFrame >= (int)m_frames.size()) {
|
|
|
|
m_frames.resize(m_currentFrame + 1);
|
|
|
|
setUnsaveState();
|
|
|
|
updateFramesSettingButton();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::syncFrameFromCurrent()
|
|
|
|
{
|
|
|
|
ensureEnoughFrames();
|
|
|
|
m_frames[m_currentFrame] = {m_currentAttributes, m_currentParameters};
|
2019-03-03 00:05:13 +00:00
|
|
|
updateFramesDurations();
|
2018-11-09 03:20:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::setFrameCount(int count)
|
|
|
|
{
|
|
|
|
if (count == (int)m_frames.size())
|
|
|
|
return;
|
|
|
|
|
|
|
|
setUnsaveState();
|
|
|
|
count = std::max(count, 1);
|
|
|
|
m_frames.resize(count);
|
2019-03-03 00:05:13 +00:00
|
|
|
updateFramesDurations();
|
2018-11-09 03:20:48 +00:00
|
|
|
updateFramesSettingButton();
|
|
|
|
if (m_currentFrame >= count) {
|
|
|
|
setCurrentFrame(count - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-03 00:05:13 +00:00
|
|
|
void PoseEditWidget::updateFramesDurations()
|
|
|
|
{
|
|
|
|
if (m_frames.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
float frameDuration = m_duration / m_frames.size();
|
|
|
|
for (auto &frame: m_frames)
|
|
|
|
frame.first["duration"] = QString::number(frameDuration);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::setDuration(float duration)
|
|
|
|
{
|
|
|
|
if (qFuzzyCompare(duration, m_duration))
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_duration = duration;
|
|
|
|
setUnsaveState();
|
|
|
|
updateFramesDurations();
|
|
|
|
}
|
|
|
|
|
2018-11-09 03:20:48 +00:00
|
|
|
void PoseEditWidget::setCurrentFrame(int frame)
|
|
|
|
{
|
|
|
|
if (m_currentFrame == frame)
|
|
|
|
return;
|
|
|
|
m_currentFrame = frame;
|
|
|
|
ensureEnoughFrames();
|
|
|
|
updateFramesSettingButton();
|
|
|
|
m_currentAttributes = m_frames[m_currentFrame].first;
|
|
|
|
m_currentParameters = m_frames[m_currentFrame].second;
|
|
|
|
updatePoseDocument();
|
|
|
|
}
|
|
|
|
|
2019-03-03 00:05:13 +00:00
|
|
|
void PoseEditWidget::insertFrameAfterCurrentFrame()
|
|
|
|
{
|
|
|
|
int currentFrame = m_currentFrame;
|
|
|
|
m_frames.resize(m_frames.size() + 1);
|
|
|
|
updateFramesDurations();
|
|
|
|
if (-1 != currentFrame) {
|
|
|
|
for (int index = m_frames.size() - 1; index > currentFrame; --index) {
|
|
|
|
m_frames[index] = m_frames[index - 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setUnsaveState();
|
|
|
|
setCurrentFrame(currentFrame + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::removeCurrentFrame()
|
|
|
|
{
|
|
|
|
if (m_frames.size() <= 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int currentFrame = m_currentFrame;
|
|
|
|
if (-1 != currentFrame) {
|
|
|
|
for (int index = currentFrame + 1; index < (int)m_frames.size(); ++index) {
|
|
|
|
m_frames[index - 1] = m_frames[index];
|
|
|
|
}
|
|
|
|
m_frames.resize(m_frames.size() - 1);
|
|
|
|
}
|
|
|
|
updateFramesDurations();
|
|
|
|
setUnsaveState();
|
|
|
|
if (currentFrame - 1 >= 0)
|
|
|
|
setCurrentFrame(currentFrame - 1);
|
|
|
|
else if (currentFrame < (int)m_frames.size()) {
|
|
|
|
m_currentFrame = -1;
|
|
|
|
setCurrentFrame(currentFrame);
|
|
|
|
} else
|
|
|
|
setCurrentFrame(0);
|
|
|
|
}
|
|
|
|
|
2018-11-05 15:47:21 +00:00
|
|
|
void PoseEditWidget::changeTurnaround()
|
2018-11-01 15:24:06 +00:00
|
|
|
{
|
2018-11-05 15:47:21 +00:00
|
|
|
QString fileName = QFileDialog::getOpenFileName(this, QString(), QString(),
|
|
|
|
tr("Image Files (*.png *.jpg *.bmp)")).trimmed();
|
|
|
|
if (fileName.isEmpty())
|
|
|
|
return;
|
|
|
|
QImage image;
|
|
|
|
if (!image.load(fileName))
|
|
|
|
return;
|
2018-11-09 03:20:48 +00:00
|
|
|
auto newImageId = ImageForever::add(&image);
|
|
|
|
if (m_imageId == newImageId)
|
|
|
|
return;
|
|
|
|
setUnsaveState();
|
|
|
|
m_imageId = newImageId;
|
2018-11-05 15:47:21 +00:00
|
|
|
m_poseDocument->updateTurnaround(image);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::updatePoseDocument()
|
|
|
|
{
|
2019-03-03 00:05:13 +00:00
|
|
|
m_otherFramesParameters.clear();
|
|
|
|
for (int i = 0; i < (int)m_frames.size(); ++i) {
|
|
|
|
if (m_currentFrame == i)
|
|
|
|
continue;
|
|
|
|
m_otherFramesParameters.push_back(m_frames[i].second);
|
|
|
|
}
|
|
|
|
m_poseDocument->updateOtherFramesParameters(m_otherFramesParameters);
|
2018-11-09 03:20:48 +00:00
|
|
|
m_poseDocument->fromParameters(m_document->resultRigBones(), m_currentParameters);
|
|
|
|
m_poseDocument->clearHistories();
|
|
|
|
m_poseDocument->saveHistoryItem();
|
2018-11-05 15:47:21 +00:00
|
|
|
updatePreview();
|
2018-11-01 15:24:06 +00:00
|
|
|
}
|
|
|
|
|
2018-09-21 07:10:18 +00:00
|
|
|
void PoseEditWidget::reject()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::closeEvent(QCloseEvent *event)
|
|
|
|
{
|
|
|
|
if (m_unsaved && !m_closed) {
|
|
|
|
QMessageBox::StandardButton answer = QMessageBox::question(this,
|
|
|
|
APP_NAME,
|
|
|
|
tr("Do you really want to close while there are unsaved changes?"),
|
|
|
|
QMessageBox::Yes | QMessageBox::No);
|
|
|
|
if (answer != QMessageBox::Yes) {
|
|
|
|
event->ignore();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_closed = true;
|
|
|
|
hide();
|
|
|
|
if (m_posePreviewManager->isRendering()) {
|
|
|
|
event->ignore();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
event->accept();
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize PoseEditWidget::sizeHint() const
|
|
|
|
{
|
2018-11-01 15:24:06 +00:00
|
|
|
return QSize(1024, 768);
|
2018-09-21 07:10:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PoseEditWidget::~PoseEditWidget()
|
2018-09-18 03:17:35 +00:00
|
|
|
{
|
|
|
|
delete m_posePreviewManager;
|
2018-11-05 15:47:21 +00:00
|
|
|
delete m_poseDocument;
|
2018-09-18 03:17:35 +00:00
|
|
|
}
|
|
|
|
|
2018-09-21 07:10:18 +00:00
|
|
|
void PoseEditWidget::updatePreview()
|
2018-09-18 03:17:35 +00:00
|
|
|
{
|
2018-09-21 07:10:18 +00:00
|
|
|
if (m_closed)
|
|
|
|
return;
|
|
|
|
|
2018-09-18 03:17:35 +00:00
|
|
|
if (m_posePreviewManager->isRendering()) {
|
|
|
|
m_isPreviewDirty = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-10-26 23:04:45 +00:00
|
|
|
const std::vector<RiggerBone> *rigBones = m_document->resultRigBones();
|
|
|
|
const std::map<int, RiggerVertexWeights> *rigWeights = m_document->resultRigWeights();
|
2018-09-18 03:17:35 +00:00
|
|
|
|
|
|
|
m_isPreviewDirty = false;
|
|
|
|
|
|
|
|
if (nullptr == rigBones || nullptr == rigWeights) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-01 15:24:06 +00:00
|
|
|
Poser *poser = newPoser(m_document->rigType, *rigBones);
|
|
|
|
if (nullptr == poser)
|
|
|
|
return;
|
|
|
|
|
2018-11-09 03:20:48 +00:00
|
|
|
poser->parameters() = m_currentParameters;
|
2018-09-21 07:10:18 +00:00
|
|
|
poser->commit();
|
2018-10-26 23:04:45 +00:00
|
|
|
m_posePreviewManager->postUpdate(*poser, m_document->currentRiggedOutcome(), *rigWeights);
|
2018-09-21 07:10:18 +00:00
|
|
|
delete poser;
|
2018-09-18 03:17:35 +00:00
|
|
|
}
|
|
|
|
|
2018-09-21 07:10:18 +00:00
|
|
|
void PoseEditWidget::setEditPoseId(QUuid poseId)
|
|
|
|
{
|
|
|
|
if (m_poseId == poseId)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_poseId = poseId;
|
|
|
|
updateTitle();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::updateTitle()
|
|
|
|
{
|
|
|
|
if (m_poseId.isNull()) {
|
|
|
|
setWindowTitle(unifiedWindowTitle(tr("New") + (m_unsaved ? "*" : "")));
|
|
|
|
return;
|
|
|
|
}
|
2018-10-25 00:19:38 +00:00
|
|
|
const Pose *pose = m_document->findPose(m_poseId);
|
2018-09-21 07:10:18 +00:00
|
|
|
if (nullptr == pose) {
|
|
|
|
qDebug() << "Find pose failed:" << m_poseId;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setWindowTitle(unifiedWindowTitle(pose->name + (m_unsaved ? "*" : "")));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::setEditPoseName(QString name)
|
|
|
|
{
|
|
|
|
m_nameEdit->setText(name);
|
|
|
|
updateTitle();
|
|
|
|
}
|
|
|
|
|
2018-11-09 03:20:48 +00:00
|
|
|
void PoseEditWidget::setEditPoseFrames(std::vector<std::pair<std::map<QString, QString>, std::map<QString, std::map<QString, QString>>>> frames)
|
2018-09-21 07:10:18 +00:00
|
|
|
{
|
2018-11-09 03:20:48 +00:00
|
|
|
m_frames = frames;
|
|
|
|
if (!m_frames.empty()) {
|
|
|
|
m_currentFrame = 0;
|
|
|
|
const auto &frame = m_frames[m_currentFrame];
|
|
|
|
m_currentAttributes = frame.first;
|
|
|
|
m_currentParameters = frame.second;
|
|
|
|
}
|
2019-03-03 00:05:13 +00:00
|
|
|
float totalDuration = 0;
|
|
|
|
for (const auto &frame: m_frames) {
|
|
|
|
float frameDuration = valueOfKeyInMapOrEmpty(frame.first, "duration").toFloat();
|
|
|
|
totalDuration += frameDuration;
|
|
|
|
}
|
|
|
|
if (qFuzzyIsNull(totalDuration))
|
|
|
|
totalDuration = 1.0;
|
|
|
|
m_durationEdit->setValue(totalDuration);
|
2018-11-05 15:47:21 +00:00
|
|
|
updatePoseDocument();
|
|
|
|
updatePreview();
|
2018-11-09 03:20:48 +00:00
|
|
|
updateFramesSettingButton();
|
2018-11-05 15:47:21 +00:00
|
|
|
m_poseDocument->saveHistoryItem();
|
|
|
|
}
|
|
|
|
|
2018-11-09 03:20:48 +00:00
|
|
|
void PoseEditWidget::setEditPoseTurnaroundImageId(QUuid imageId)
|
2018-11-05 15:47:21 +00:00
|
|
|
{
|
2018-11-09 03:20:48 +00:00
|
|
|
m_imageId = imageId;
|
|
|
|
const auto &image = ImageForever::get(m_imageId);
|
|
|
|
if (nullptr == image)
|
|
|
|
return;
|
|
|
|
m_poseDocument->updateTurnaround(*image);
|
2018-09-21 07:10:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void PoseEditWidget::clearUnsaveState()
|
|
|
|
{
|
|
|
|
m_unsaved = false;
|
|
|
|
updateTitle();
|
|
|
|
}
|
|
|
|
|
2018-11-09 03:20:48 +00:00
|
|
|
void PoseEditWidget::setUnsaveState()
|
|
|
|
{
|
|
|
|
m_unsaved = true;
|
|
|
|
updateTitle();
|
|
|
|
}
|
|
|
|
|
2018-09-21 07:10:18 +00:00
|
|
|
void PoseEditWidget::save()
|
|
|
|
{
|
|
|
|
if (m_poseId.isNull()) {
|
2018-11-09 15:07:26 +00:00
|
|
|
m_poseId = QUuid::createUuid();
|
|
|
|
emit addPose(m_poseId, m_nameEdit->text(), m_frames, m_imageId);
|
2018-09-21 07:10:18 +00:00
|
|
|
} else if (m_unsaved) {
|
|
|
|
emit renamePose(m_poseId, m_nameEdit->text());
|
2018-11-09 03:20:48 +00:00
|
|
|
emit setPoseFrames(m_poseId, m_frames);
|
|
|
|
emit setPoseTurnaroundImageId(m_poseId, m_imageId);
|
2018-09-21 07:10:18 +00:00
|
|
|
}
|
2018-11-09 15:07:26 +00:00
|
|
|
clearUnsaveState();
|
2018-09-18 03:17:35 +00:00
|
|
|
}
|