Skip to content

Commit

Permalink
Add Auto Export feature
Browse files Browse the repository at this point in the history
Closes #11
  • Loading branch information
mitchcurtis committed Dec 27, 2017
1 parent 484565f commit 2f72c28
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 11 deletions.
54 changes: 47 additions & 7 deletions app/layeredimageproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@

LayeredImageProject::LayeredImageProject() :
mCurrentLayerIndex(0),
mLayersCreated(0)
mLayersCreated(0),
mAutoExportEnabled(false)
{
setObjectName(QLatin1String("LayeredImageProject"));
qCDebug(lcProjectLifecycle) << "constructing" << this;
Expand Down Expand Up @@ -135,6 +136,32 @@ QImage LayeredImageProject::flattenedImage(std::function<QImage(int)> layerSubst
return finalImage;
}

bool LayeredImageProject::isAutoExportEnabled() const
{
return mAutoExportEnabled;
}

void LayeredImageProject::setAutoExportEnabled(bool autoExportEnabled)
{
if (autoExportEnabled == mAutoExportEnabled)
return;

mAutoExportEnabled = autoExportEnabled;
emit autoExportEnabledChanged();
}

QString LayeredImageProject::autoExportFilePath(const QUrl &projectUrl)
{
const QString filePath = projectUrl.toLocalFile();
QString path = filePath;
const int lastPeriodIndex = filePath.lastIndexOf(QLatin1Char('.'));
if (lastPeriodIndex != -1)
path.replace(lastPeriodIndex + 1, filePath.size() - lastPeriodIndex - 1, QLatin1String("png"));
else
path.append(QLatin1String("png"));
return path;
}

void LayeredImageProject::createNew(int imageWidth, int imageHeight, bool transparentBackground)
{
if (hasLoaded()) {
Expand Down Expand Up @@ -195,6 +222,9 @@ void LayeredImageProject::load(const QUrl &url)
}

readGuides(projectObject);

mAutoExportEnabled = projectObject.value("autoExportEnabled").toBool(false);

mCachedProjectJson = projectObject;

setUrl(url);
Expand Down Expand Up @@ -280,6 +310,13 @@ void LayeredImageProject::saveAs(const QUrl &url)
writeGuides(projectObject);
emit readyForWritingToJson(&projectObject);

if (mAutoExportEnabled) {
projectObject.insert("autoExportEnabled", mAutoExportEnabled);

if (!exportImage(QUrl::fromLocalFile(autoExportFilePath(url))))
return;
}

rootJson.insert("project", projectObject);

QJsonDocument jsonDoc(rootJson);
Expand All @@ -305,27 +342,30 @@ void LayeredImageProject::saveAs(const QUrl &url)
mHadUnsavedChangesBeforeMacroBegan = false;
}

void LayeredImageProject::exportImage(const QUrl &url)
// Returns true because the auto-export feature in saveAs() needs to know whether or not it should return early.
bool LayeredImageProject::exportImage(const QUrl &url)
{
if (!hasLoaded())
return;
return false;

if (url.isEmpty())
return;
return false;

const QString filePath = url.toLocalFile();
const QFileInfo projectSaveFileInfo(filePath);
if (mTempDir.isValid()) {
if (projectSaveFileInfo.dir().path() == mTempDir.path()) {
error(QLatin1String("Cannot save project in internal temporary directory"));
return;
return false;
}
}

if (!flattenedImage().save(filePath)) {
error(QString::fromLatin1("Failed to save project's image to:\n\n%1").arg(filePath));
return;
return false;
}

return true;
}

void LayeredImageProject::resize(int width, int height)
Expand Down Expand Up @@ -543,7 +583,7 @@ ImageLayer *LayeredImageProject::takeLayer(int index)
QDebug operator<<(QDebug debug, const LayeredImageProject *project)
{
debug.nospace() << "LayeredImageProject currentLayerIndex=" << project->mCurrentLayerIndex
<< ", layers:";
<< ", layers:";
foreach (ImageLayer *layer, project->mLayers) {
debug << "\n name=" << layer->name()
<< " visible=" << layer->isVisible()
Expand Down
9 changes: 8 additions & 1 deletion app/layeredimageproject.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class LayeredImageProject : public Project
Q_PROPERTY(int currentLayerIndex READ currentLayerIndex WRITE setCurrentLayerIndex NOTIFY currentLayerIndexChanged)
Q_PROPERTY(ImageLayer *currentLayer READ currentLayer NOTIFY postCurrentLayerChanged)
Q_PROPERTY(int layerCount READ layerCount NOTIFY layerCountChanged)
Q_PROPERTY(bool autoExportEnabled READ isAutoExportEnabled WRITE setAutoExportEnabled NOTIFY autoExportEnabledChanged)

public:
LayeredImageProject();
Expand All @@ -53,11 +54,16 @@ class LayeredImageProject : public Project

QImage flattenedImage(std::function<QImage(int)> layerSubstituteFunction = nullptr) const;

bool isAutoExportEnabled() const;
void setAutoExportEnabled(bool autoExportEnabled);
static QString autoExportFilePath(const QUrl &projectUrl);

signals:
void currentLayerIndexChanged();
void preCurrentLayerChanged();
void postCurrentLayerChanged();
void layerCountChanged();
void autoExportEnabledChanged();

void preLayersCleared();
void postLayersCleared();
Expand All @@ -74,7 +80,7 @@ public slots:
void load(const QUrl &url) override;
void close() override;
void saveAs(const QUrl &url) override;
void exportImage(const QUrl &url);
bool exportImage(const QUrl &url);
void resize(int width, int height);

void addNewLayer();
Expand Down Expand Up @@ -113,6 +119,7 @@ public slots:
int mCurrentLayerIndex;
// Give each layer a unique name based on the layers created so far.
int mLayersCreated;
bool mAutoExportEnabled;
};

#endif // LAYEREDIMAGEPROJECT_H
12 changes: 11 additions & 1 deletion app/qml/ui/+windows/MenuBar.qml
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,22 @@ MenuBar {
}

MenuItem {
id: exportMenuButton
objectName: "exportMenuButton"
text: qsTr("Export")
enabled: project ? project.loaded && projectType === Project.LayeredImageType : false
enabled: project && project.loaded && projectType === Project.LayeredImageType
onClicked: exportDialog.open()
}

MenuItem {
objectName: "autoExportMenuButton"
text: qsTr("Auto Export")
checkable: true
checked: enabled && project.autoExportEnabled
enabled: exportMenuButton.enabled
onClicked: project.autoExportEnabled = !project.autoExportEnabled
}

MenuItem {
objectName: "closeMenuButton"
text: qsTr("Close")
Expand Down
12 changes: 11 additions & 1 deletion app/qml/ui/MenuBar.qml
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,22 @@ Item {
}

Platform.MenuItem {
id: exportMenuButton
objectName: "exportMenuButton"
text: qsTr("Export")
enabled: project ? project.loaded && projectType === Project.LayeredImageType : false
enabled: project && project.loaded && projectType === Project.LayeredImageType
onTriggered: exportDialog.open()
}

Platform.MenuItem {
objectName: "autoExportMenuButton"
text: qsTr("Auto Export")
checkable: true
checked: enabled && project.autoExportEnabled
enabled: exportMenuButton.enabled
onTriggered: project.autoExportEnabled = !project.autoExportEnabled
}

Platform.MenuItem {
objectName: "closeMenuButton"
text: qsTr("Close")
Expand Down
3 changes: 2 additions & 1 deletion tests/testhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ void TestHelper::changeToolSize(int size)
++sliderHandlePos.rx()) {
QTest::mouseMove(toolSizeSlider->window(), sliderHandlePos, 5);
}
--sliderHandlePos.rx();
QTest::mouseRelease(toolSizeSlider->window(), Qt::LeftButton, Qt::NoModifier, sliderHandlePos);
QCOMPARE(toolSizeSlider->property("pressed").toBool(), false);
QCOMPARE(sliderValue(toolSizeSlider), size);
Expand Down Expand Up @@ -725,7 +726,7 @@ void TestHelper::triggerShortcut(const QString &objectName, const QString &seque
QSignalSpy activatedSpy(shortcut, SIGNAL(activated()));
QVERIFY(activatedSpy.isValid());

QTest::qWaitForWindowActive(window);
QVERIFY(QTest::qWaitForWindowActive(window));
const int value = QKeySequence(sequenceAsString)[0];
Qt::KeyboardModifiers mods = (Qt::KeyboardModifiers)(value & Qt::KeyboardModifierMask);
QTest::keyClick(window, value & ~mods, mods);
Expand Down
59 changes: 59 additions & 0 deletions tests/tst_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ private Q_SLOTS:
void layerVisibilityAfterMoving();
void undoAfterAddLayer();
void selectionConfirmedWhenSwitchingLayers();
void autoExport();
};

tst_App::tst_App(int &argc, char **argv) :
Expand Down Expand Up @@ -3023,6 +3024,64 @@ void tst_App::selectionConfirmedWhenSwitchingLayers()
QCOMPARE(snapshotAfterSwitchingLayers.pixelColor(0, 0), Qt::red);
}

void tst_App::autoExport()
{
// Create a new layered image project with the dimensions of the clipboard contents.
createNewLayeredImageProject(10, 10);

panTopLeftTo(0, 0);

QCOMPARE(layeredImageProject->isAutoExportEnabled(), false);

// Don't have a shortcut for it yet, so have to change it manually, but we can still
// check that the menus update accordingly.
#ifdef NON_NATIVE_MENUS
// TODO: can we just use the QObject-based code below instead?
QQuickItem *autoExportMenuButton = window->findChild<QQuickItem*>("autoExportMenuButton");
QVERIFY(autoExportMenuButton);
#else
QObject *autoExportMenuButton = window->findChild<QObject*>("autoExportMenuButton");
QVERIFY(autoExportMenuButton);
#endif
QCOMPARE(autoExportMenuButton->property("checked"), false);

layeredImageProject->setAutoExportEnabled(true);
QCOMPARE(autoExportMenuButton->property("checked"), true);

QCOMPARE(layeredImageProject->canSave(), true);

// Save the project so that the auto-export is triggered.
const QString savedProjectPath = tempProjectDir->path() + "/autoExport-project.slp";
layeredImageProject->saveAs(QUrl::fromLocalFile(savedProjectPath));

// The image file should exist now.
const QString autoExportFilePath = LayeredImageProject::autoExportFilePath(layeredImageProject->url());
QVERIFY(QFile::exists(autoExportFilePath));

QImage exportedImage(autoExportFilePath);
QVERIFY(!exportedImage.isNull());

QImage expectedExportedImage(10, 10, QImage::Format_ARGB32);
expectedExportedImage.fill(Qt::white);
QCOMPARE(exportedImage, expectedExportedImage);

// Disable auto-export.
layeredImageProject->setAutoExportEnabled(false);
QCOMPARE(autoExportMenuButton->property("checked"), false);

// Draw something.
setCursorPosInScenePixels(2, 2);
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, cursorWindowPos);
QCOMPARE(layeredImageProject->currentLayer()->image()->pixelColor(2, 2), Qt::black);

// Save again.
layeredImageProject->saveAs(QUrl::fromLocalFile(savedProjectPath));

// No export should have happened and so the exported image shouldn't have changed.
exportedImage = QImage(autoExportFilePath);
QCOMPARE(exportedImage, expectedExportedImage);
}

int main(int argc, char *argv[])
{
tst_App test(argc, argv);
Expand Down

0 comments on commit 2f72c28

Please sign in to comment.