diff --git a/.gitignore b/.gitignore index 3ab8784078c0af970f261f7dcb46450e62e8cebe..25057c7a145d9e84bf4067d34f0c2a44a1e047b5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,9 @@ /bin-Release/ /build-Debug/ /build-Release/ +/CodeBackup_d.lst +/CodeBackup_r+.lst +/CodeBackup_r-.lst +/CreateCodeBackup.cmd +/Grinder.smproj +/Grinder.tdl diff --git a/Grinder/Grinder.pro b/Grinder/Grinder.pro index 4841788786e77f0640974dc569ee559e8afeda8a..d00773d77a56f0876ebe0b6b552c8cac4d2eb4e5 100644 --- a/Grinder/Grinder.pro +++ b/Grinder/Grinder.pro @@ -100,24 +100,24 @@ SOURCES += \ engine/processors/ConvertToGrayscaleProcessor.cpp \ ui/graph/GraphLayout.cpp \ core/GrinderExceptions.cpp \ - project/serialization/SettingsContainer.cpp \ - project/serialization/SerializationExceptions.cpp \ - project/serialization/SettingsCodec.cpp \ - project/serialization/JsonSettingsCodec.cpp \ - project/serialization/ProjectSerializer.cpp \ - project/serialization/SerializationContext.cpp \ - project/serialization/DeserializationContext.cpp \ + common/serialization/SettingsContainer.cpp \ + common/serialization/SerializationExceptions.cpp \ + common/serialization/SettingsCodec.cpp \ + common/serialization/JsonSettingsCodec.cpp \ + project/ProjectSerializer.cpp \ + common/serialization/SerializationContext.cpp \ + common/serialization/DeserializationContext.cpp \ util/SerializationUtils.cpp \ common/MRUStringList.cpp \ ui/mainwnd/RecentProjectsMenu.cpp \ ui/dlg/OptionsDialog.cpp \ - ui/property/PropertyTreeWidget.cpp \ - ui/property/PropertyTreeItem.cpp \ - ui/property/BlockPropertyTreeItem.cpp \ - ui/property/PropertyTreeItemDelegate.cpp \ - ui/property/ValuePropertyTreeItem.cpp \ - ui/property/editors/BoolPropertyEditor.cpp \ - ui/property/editors/TextPropertyEditor.cpp \ + ui/properties/PropertyTreeWidget.cpp \ + ui/properties/PropertyTreeItem.cpp \ + ui/properties/BlockPropertyTreeItem.cpp \ + ui/properties/PropertyTreeItemDelegate.cpp \ + ui/properties/ValuePropertyTreeItem.cpp \ + ui/properties/editors/BoolPropertyEditor.cpp \ + ui/properties/editors/TextPropertyEditor.cpp \ pipeline/blocks/OutputBlock.cpp \ pipeline/blocks/InputBlock.cpp \ image/ImageBuild.cpp \ @@ -139,19 +139,19 @@ SOURCES += \ image/LayerVector.cpp \ ui/image/LayersListWidget.cpp \ ui/image/LayersListItem.cpp \ - common/properties/BoolProperty.cpp \ - common/properties/IntProperty.cpp \ - common/properties/RealProperty.cpp \ - common/properties/StringProperty.cpp \ - common/properties/UIntProperty.cpp \ - common/PropertyBase.cpp \ - common/PropertyExceptions.cpp \ - common/PropertyID.cpp \ - common/PropertyVector.cpp \ + common/properties/types/BoolProperty.cpp \ + common/properties/types/IntProperty.cpp \ + common/properties/types/RealProperty.cpp \ + common/properties/types/StringProperty.cpp \ + common/properties/types/UIntProperty.cpp \ + common/properties/PropertyBase.cpp \ + common/properties/PropertyExceptions.cpp \ + common/properties/PropertyID.cpp \ + common/properties/PropertyVector.cpp \ image/DraftItemType.cpp \ image/DraftItem.cpp \ - common/properties/PointProperty.cpp \ - common/properties/ColorProperty.cpp \ + common/properties/types/PointProperty.cpp \ + common/properties/types/ColorProperty.cpp \ image/DraftItemVector.cpp \ image/DraftItemCatalog.cpp \ image/draftitems/BoxDraftItem.cpp \ @@ -159,7 +159,7 @@ SOURCES += \ image/draftitems/BoxDraftItemRenderer.cpp \ image/draftitems/LineDraftItemRenderer.cpp \ image/DraftItemRendererBase.cpp \ - common/properties/SizeProperty.cpp \ + common/properties/types/SizeProperty.cpp \ ui/visscene/VisualNode.cpp \ ui/image/ImageEditorNode.cpp \ ui/image/DraftItemNode.cpp \ @@ -173,14 +173,14 @@ SOURCES += \ ui/image/draftitems/BoxDraftItemNode.cpp \ ui/image/draftitems/LineDraftItemNode.cpp \ ui/image/ImageEditorPropertyWidget.cpp \ - ui/property/editors/PointPropertyEditor.cpp \ - ui/property/editors/SizePropertyEditor.cpp \ + ui/properties/editors/PointPropertyEditor.cpp \ + ui/properties/editors/SizePropertyEditor.cpp \ ui/widget/AutoFocusLineEdit.cpp \ ui/widget/ColorWidget.cpp \ ui/image/tools/ColorPickerTool.cpp \ ui/widget/GrinderDockWidget.cpp \ - common/properties/AngleProperty.cpp \ - ui/property/editors/AnglePropertyEditor.cpp \ + common/properties/types/AngleProperty.cpp \ + ui/properties/editors/AnglePropertyEditor.cpp \ ui/image/ImageEditorEnvironment.cpp \ util/MathUtils.cpp \ ui/image/ImageEditorWidget.cpp \ @@ -189,10 +189,12 @@ SOURCES += \ ui/image/InPlaceEditorDragHandle.cpp \ ui/image/InPlaceEditor.cpp \ ui/image/editors/LinearInPlaceEditor.cpp \ - common/PropertyObject.cpp \ + common/properties/PropertyObject.cpp \ ui/image/editors/RectangularInPlaceEditor.cpp \ ui/visscene/VisualSceneInputHandler.cpp \ - ui/image/ColorPresetsWidget.cpp + ui/image/ColorPresetsWidget.cpp \ + common/serialization/SerializationBase.cpp \ + core/ClipboardManager.cpp HEADERS += \ ui/mainwnd/GrinderWindow.h \ @@ -279,14 +281,14 @@ HEADERS += \ engine/processors/ConvertToGrayscaleProcessor.h \ ui/graph/GraphLayout.h \ core/GrinderExceptions.h \ - project/serialization/SettingsContainer.h \ - project/serialization/SerializationExceptions.h \ - project/serialization/SettingsContainer.impl.h \ - project/serialization/SettingsCodec.h \ - project/serialization/JsonSettingsCodec.h \ - project/serialization/ProjectSerializer.h \ - project/serialization/SerializationContext.h \ - project/serialization/DeserializationContext.h \ + common/serialization/SettingsContainer.h \ + common/serialization/SerializationExceptions.h \ + common/serialization/SettingsContainer.impl.h \ + common/serialization/SettingsCodec.h \ + common/serialization/JsonSettingsCodec.h \ + project/ProjectSerializer.h \ + common/serialization/SerializationContext.h \ + common/serialization/DeserializationContext.h \ util/SerializationUtils.h \ util/SerializationUtils.impl.h \ common/MRUStringList.h \ @@ -294,21 +296,21 @@ HEADERS += \ ui/mainwnd/RecentProjectsMenu.h \ ui/dlg/OptionsDialog.h \ util/StringUtils.impl.h \ - ui/property/PropertyTreeWidget.h \ - ui/property/PropertyTreeItem.h \ - ui/property/BlockPropertyTreeItem.h \ - ui/property/PropertyTreeItemDelegate.h \ - ui/property/ValuePropertyTreeItem.h \ - ui/property/PropertyEditor.h \ - ui/property/PropertyEditor.impl.h \ - ui/property/editors/BoolPropertyEditor.h \ - ui/property/editors/TextPropertyEditor.h \ + ui/properties/PropertyTreeWidget.h \ + ui/properties/PropertyTreeItem.h \ + ui/properties/BlockPropertyTreeItem.h \ + ui/properties/PropertyTreeItemDelegate.h \ + ui/properties/ValuePropertyTreeItem.h \ + ui/properties/PropertyEditor.h \ + ui/properties/PropertyEditor.impl.h \ + ui/properties/editors/BoolPropertyEditor.h \ + ui/properties/editors/TextPropertyEditor.h \ res/Resources.h \ pipeline/blocks/OutputBlock.h \ pipeline/blocks/InputBlock.h \ image/ImageBuild.h \ - project/serialization/SerializationContext.impl.h \ - project/serialization/DeserializationContext.impl.h \ + common/serialization/SerializationContext.impl.h \ + common/serialization/DeserializationContext.impl.h \ image/ImageBuildVector.h \ image/ImageBuildPool.h \ image/ImageExceptions.h \ @@ -332,27 +334,27 @@ HEADERS += \ image/LayerVector.h \ ui/image/LayersListWidget.h \ ui/image/LayersListItem.h \ - common/properties/BoolProperty.h \ - common/properties/IntProperty.h \ - common/properties/RangeConstraint.h \ - common/properties/RangeConstraint.impl.h \ - common/properties/RealProperty.h \ + common/properties/types/BoolProperty.h \ + common/properties/types/IntProperty.h \ + common/properties/types/RangeConstraint.h \ + common/properties/types/RangeConstraint.impl.h \ + common/properties/types/RealProperty.h \ common/properties/StandardProperties.h \ - common/properties/StringProperty.h \ - common/properties/UIntProperty.h \ - common/Property.h \ - common/Property.impl.h \ - common/PropertyBase.h \ - common/PropertyConstraint.h \ - common/PropertyConstraint.impl.h \ - common/PropertyExceptions.h \ - common/PropertyID.h \ - common/PropertyVector.h \ - common/PropertyVector.impl.h \ + common/properties/types/StringProperty.h \ + common/properties/types/UIntProperty.h \ + common/properties/Property.h \ + common/properties/Property.impl.h \ + common/properties/PropertyBase.h \ + common/properties/PropertyConstraint.h \ + common/properties/PropertyConstraint.impl.h \ + common/properties/PropertyExceptions.h \ + common/properties/PropertyID.h \ + common/properties/PropertyVector.h \ + common/properties/PropertyVector.impl.h \ image/DraftItemType.h \ image/DraftItem.h \ - common/properties/PointProperty.h \ - common/properties/ColorProperty.h \ + common/properties/types/PointProperty.h \ + common/properties/types/ColorProperty.h \ image/DraftItemVector.h \ image/DraftItemCatalog.h \ image/draftitems/BoxDraftItem.h \ @@ -362,7 +364,7 @@ HEADERS += \ image/draftitems/LineDraftItemRenderer.h \ image/DraftItemRenderer.impl.h \ image/DraftItemRendererBase.h \ - common/properties/SizeProperty.h \ + common/properties/types/SizeProperty.h \ ui/visscene/VisualNode.h \ ui/image/ImageEditorNode.h \ ui/visscene/VisualNodeFactory.h \ @@ -380,16 +382,16 @@ HEADERS += \ ui/image/draftitems/BoxDraftItemNode.h \ ui/image/draftitems/LineDraftItemNode.h \ ui/image/ImageEditorPropertyWidget.h \ - ui/property/editors/DualTextPropertyEditor.h \ - ui/property/editors/PointPropertyEditor.h \ - ui/property/editors/DualTextPropertyEditor.impl.h \ - ui/property/editors/SizePropertyEditor.h \ + ui/properties/editors/DualTextPropertyEditor.h \ + ui/properties/editors/PointPropertyEditor.h \ + ui/properties/editors/DualTextPropertyEditor.impl.h \ + ui/properties/editors/SizePropertyEditor.h \ ui/widget/AutoFocusLineEdit.h \ ui/widget/ColorWidget.h \ ui/image/tools/ColorPickerTool.h \ ui/widget/GrinderDockWidget.h \ - common/properties/AngleProperty.h \ - ui/property/editors/AnglePropertyEditor.h \ + common/properties/types/AngleProperty.h \ + ui/properties/editors/AnglePropertyEditor.h \ ui/image/ImageEditorEnvironment.h \ util/MathUtils.h \ ui/image/ImageEditorWidget.h \ @@ -398,12 +400,15 @@ HEADERS += \ ui/image/InPlaceEditorDragHandle.h \ ui/image/InPlaceEditor.h \ ui/image/editors/LinearInPlaceEditor.h \ - common/PropertyObject.h \ - common/PropertyObject.impl.h \ + common/properties/PropertyObject.h \ + common/properties/PropertyObject.impl.h \ ui/image/editors/RectangularInPlaceEditor.h \ ui/visscene/VisualSceneInputHandler.h \ ui/visscene/VisualSceneInputHandler.impl.h \ - ui/image/ColorPresetsWidget.h + ui/image/ColorPresetsWidget.h \ + common/serialization/SerializationBase.h \ + core/ClipboardManager.h \ + core/ClipboardManager.impl.h FORMS += \ ui/mainwnd/GrinderWindow.ui \ diff --git a/Grinder/Version.h b/Grinder/Version.h index 1ebf1c94cd8dd20b8e8c5f0228691d4b3bab65cf..e7ead7212016287f8a21a69478975bcf8bc767e6 100644 --- a/Grinder/Version.h +++ b/Grinder/Version.h @@ -10,14 +10,14 @@ #define GRNDR_INFO_TITLE "Grinder" #define GRNDR_INFO_COPYRIGHT "Copyright (c) WWU Muenster" -#define GRNDR_INFO_DATE "03.04.2018" +#define GRNDR_INFO_DATE "06.04.2018" #define GRNDR_INFO_COMPANY "WWU Muenster" #define GRNDR_INFO_WEBSITE "http://www.uni-muenster.de" #define GRNDR_VERSION_MAJOR 0 #define GRNDR_VERSION_MINOR 2 #define GRNDR_VERSION_REVISION 0 -#define GRNDR_VERSION_BUILD 125 +#define GRNDR_VERSION_BUILD 129 namespace grndr { diff --git a/Grinder/common/ObjectVector.h b/Grinder/common/ObjectVector.h index 339f31bb8ece302fd4fcab0f863cd94c758bfe06..0ca1f04655236adb33a01e1484bd62b0b9096630 100644 --- a/Grinder/common/ObjectVector.h +++ b/Grinder/common/ObjectVector.h @@ -9,8 +9,8 @@ #include <vector> #include <memory> -#include "project/serialization/SerializationContext.h" -#include "project/serialization/DeserializationContext.h" +#include "common/serialization/SerializationContext.h" +#include "common/serialization/DeserializationContext.h" namespace grndr { diff --git a/Grinder/common/properties/StandardProperties.h b/Grinder/common/properties/StandardProperties.h index 4dd7d4078fc7b9e0149810fa86895506898fac38..207721a29d2fc1916990ff099b12d6e6ea1c283f 100644 --- a/Grinder/common/properties/StandardProperties.h +++ b/Grinder/common/properties/StandardProperties.h @@ -6,14 +6,16 @@ #ifndef STANDARDPROPERTIES_H #define STANDARDPROPERTIES_H -#include "BoolProperty.h" -#include "IntProperty.h" -#include "RealProperty.h" -#include "AngleProperty.h" -#include "StringProperty.h" -#include "UIntProperty.h" -#include "PointProperty.h" -#include "SizeProperty.h" -#include "ColorProperty.h" +#include "types/BoolProperty.h" +#include "types/IntProperty.h" +#include "types/RealProperty.h" +#include "types/AngleProperty.h" +#include "types/StringProperty.h" +#include "types/UIntProperty.h" +#include "types/PointProperty.h" +#include "types/SizeProperty.h" +#include "types/ColorProperty.h" + +#include "types/RangeConstraint.h" #endif diff --git a/Grinder/controller/ImageEditorController.cpp b/Grinder/controller/ImageEditorController.cpp index ce5c25d19818ac6614431b3b70f951a01ac27ae5..ae08544291ce13c6d1a5197285f3f4e86aca22c7 100644 --- a/Grinder/controller/ImageEditorController.cpp +++ b/Grinder/controller/ImageEditorController.cpp @@ -92,6 +92,51 @@ void ImageEditorController::switchLayer(Layer* layer) emit layerSwitched(_activeLayer); } +void ImageEditorController::copyImageBuild() const +{ + callControllerFunction("Copying image build", [this]() { + grinder()->clipboardManager().serialize<ImageBuild>(ImageBuildVector::Serialization_Element, {_activeImageBuild}); + return true; + }); +} + +void ImageEditorController::pasteImageBuild() +{ + callControllerFunction("Pasting an image build", [this]() { + clearImageBuild(_activeImageBuild); + + grinder()->clipboardManager().deserialize<ImageBuild>(ImageBuildVector::Serialization_Element, [this](const SettingsContainer& settings) { + Q_UNUSED(settings); + return _activeImageBuild; + }); + + return true; + }); +} + +void ImageEditorController::duplicateImageBuild() +{ + // TODO: Flexibler machen, kein erzwungenes Switching; verstecktes Execute mit naechstem Bild + auto clipboardContent = grinder()->clipboardManager().saveClipboard(); // Save the current clipboard contents to restore them afterwards + copyImageBuild(); + + // Switch to the next image reference + auto currentImageRef = grinder()->projectController().activeImageReference(); + grinder()->projectController().switchToNextImageReference(); + + if (grinder()->projectController().activeImageReference() != currentImageRef) // Did the image reference really change? + { + // Execute the active pipeline with the new image reference + if (const auto activeLabel = grinder()->projectController().activeLabel()) + grinder()->engineController().executeLabel(activeLabel, Engine::ExecutionMode::View); + + // The active image build has now been switched, so paste the previously copied image build + pasteImageBuild(); + } + + grinder()->clipboardManager().restoreClipboard(clipboardContent); +} + std::shared_ptr<Layer> ImageEditorController::createLayer(QString name) const { return callControllerFunction("Creating a new layer", [this](QString name) { @@ -99,6 +144,40 @@ std::shared_ptr<Layer> ImageEditorController::createLayer(QString name) const }, name); } +void ImageEditorController::copyLayer(const Layer* layer) const +{ + callControllerFunction("Copying a layer", [this](const Layer* layer) { + grinder()->clipboardManager().serialize<Layer>(LayerVector::Serialization_Element, {layer}); + return true; + }, layer); +} + +void ImageEditorController::pasteLayer() +{ + callControllerFunction("Pasting a layer", [this]() { + auto layers = grinder()->clipboardManager().deserialize<Layer>(LayerVector::Serialization_Element, [this](const SettingsContainer& settings) { + QString name = settings[Layer::Serialization_Value_Name].toString(); + + if (_activeImageBuild->layers().selectByName(name)) + name += " - Copy"; + + return createLayer(name).get(); + }); + + // Switch to the last pasted layer + if (!layers.empty()) + switchLayer(layers.back()); + + return true; + }); +} + +void ImageEditorController::cutLayer(const Layer* layer) +{ + copyLayer(layer); + removeLayer(layer); +} + void ImageEditorController::removeLayer(const Layer* layer) { if (layer) @@ -117,19 +196,21 @@ void ImageEditorController::removeLayer(const Layer* layer) void ImageEditorController::removeAllLayers() { if (_activeImageBuild) - { - auto layers = _activeImageBuild->layers(); - - for (const auto& layer : layers) - removeLayer(layer.get()); - } + _activeImageBuild->removeAllLayers(); } -std::shared_ptr<DraftItem> ImageEditorController::createDraftItem(DraftItemType type, Layer* layer) const +std::shared_ptr<DraftItem> ImageEditorController::createDraftItem(DraftItemType type, Layer* layer, bool autoCreateLayer) { if (!layer) layer = _activeLayer; + // If there are no layers, create a default one and activate it + if (!layer && autoCreateLayer) + { + layer = createLayer("Default layer").get(); + switchLayer(layer); + } + if (layer) { return callControllerFunction("Creating a draft item", [this](DraftItemType type, Layer* layer) { @@ -192,6 +273,56 @@ void ImageEditorController::setNodesPrimaryColor(QColor color, const std::vector } } +void ImageEditorController::copySelectedNodes() const +{ + callControllerFunction("Copying draft items", [this]() { + // Copy all selected draft items + auto selItems = _activeScene->getNodes<DraftItemNode>(true); + std::vector<const DraftItem*> draftItems; + + for (const auto& node : selItems) + { + if (const auto& item = node->draftItem().lock()) + draftItems.push_back(item.get()); + } + + grinder()->clipboardManager().serialize(DraftItemVector::Serialization_Element, draftItems); + return true; + }); +} + +void ImageEditorController::pasteSelectedNodes() +{ + callControllerFunction("Pasting draft items", [this]() { + // Create draft items for each object in the clipboard + auto draftItems = grinder()->clipboardManager().deserialize<DraftItem>(DraftItemVector::Serialization_Element, [this](const SettingsContainer& settings) { + DraftItemType type = settings[DraftItem::Serialization_Value_Type].toString(); + + return createDraftItem(type).get(); + }); + + // Select the pasted items + if (!draftItems.empty()) + { + _activeScene->clearSelection(); + + for (const auto& draftItem : draftItems) + { + if (auto draftItemNode = _activeScene->findDraftItemNode(draftItem)) + draftItemNode->setSelected(true); + } + } + + return true; + }); +} + +void ImageEditorController::cutSelectedNodes() const +{ + copySelectedNodes(); + removeSelectedNodes(); +} + void ImageEditorController::removeSelectedNodes() const { if (_activeScene) @@ -214,6 +345,24 @@ void ImageEditorController::controllerFunctionCalled() const _activeScene->update(); } +void ImageEditorController::clearImageBuild(ImageBuild* imageBuild) +{ + callControllerFunction("Clearing an image build", [this](ImageBuild* imageBuild) { + if (_activeImageBuild == imageBuild) + { + // Refocus the scene view before clearing everything + if (_activeScene) + _activeScene->view()->setFocus(); + + // Also unset any active layer + switchLayer(nullptr); + } + + imageBuild->removeAllLayers(); + return true; + }, imageBuild); +} + void ImageEditorController::validateLayerName(QString name, const Layer* layer) const { auto imageBuild = layer ? layer->imageBuild() : _activeImageBuild; diff --git a/Grinder/controller/ImageEditorController.h b/Grinder/controller/ImageEditorController.h index 73ae110dacf7db95c84f5f557b7a6a455e8aea73..d249368450f56dada5808fa5757a68ca19bd64d8 100644 --- a/Grinder/controller/ImageEditorController.h +++ b/Grinder/controller/ImageEditorController.h @@ -42,11 +42,18 @@ namespace grndr const Layer* activeLayer() const { return _activeLayer; } public: + void copyImageBuild() const; + void pasteImageBuild(); + void duplicateImageBuild(); + std::shared_ptr<Layer> createLayer(QString name = "") const; + void copyLayer(const Layer* layer) const; + void pasteLayer(); + void cutLayer(const Layer* layer); void removeLayer(const Layer* layer); void removeAllLayers(); - std::shared_ptr<DraftItem> createDraftItem(DraftItemType type, Layer* layer = nullptr) const; + std::shared_ptr<DraftItem> createDraftItem(DraftItemType type, Layer* layer = nullptr, bool autoCreateLayer = true); void removeDraftItem(const DraftItem* item) const; void setLayerVisibility(Layer* layer, bool visible) const; @@ -56,6 +63,9 @@ namespace grndr void setNodesPrimaryColor(QColor color, const std::vector<DraftItemNode*>& nodes) const; + void copySelectedNodes() const; + void pasteSelectedNodes(); + void cutSelectedNodes() const; void removeSelectedNodes() const; signals: @@ -69,6 +79,8 @@ namespace grndr virtual void controllerFunctionCalled() const override; private: + void clearImageBuild(ImageBuild* imageBuild); + void validateLayerName(QString name, const Layer* layer = nullptr) const; void connectLayerSignals(ImageBuild* imageBuild, bool connectSignals = true) const; diff --git a/Grinder/controller/ProjectController.cpp b/Grinder/controller/ProjectController.cpp index f8efed3f663b65658a36819c0249cd608796b33d..ca9f9c1e84e95d0930fe5187ece0f0797c5019f6 100644 --- a/Grinder/controller/ProjectController.cpp +++ b/Grinder/controller/ProjectController.cpp @@ -9,8 +9,8 @@ #include "core/GrinderApplication.h" #include "project/Project.h" #include "project/ProjectExceptions.h" -#include "project/serialization/JsonSettingsCodec.h" -#include "project/serialization/ProjectSerializer.h" +#include "project/ProjectSerializer.h" +#include "common/serialization/JsonSettingsCodec.h" #include "ui/mainwnd/LabelsListWidget.h" #include "ui/mainwnd/ImageReferencesListWidget.h" #include "util/ImageUtils.h" @@ -174,6 +174,18 @@ void ProjectController::switchImageReference(ImageReference* imageRef) emit imageReferenceSwitched(_activeImageReference); } +void ProjectController::switchToNextImageReference() +{ + if (auto nextImageRef = getNeighboringImageReference(false)) + switchImageReference(nextImageRef); +} + +void ProjectController::switchToPreviousImageReference() +{ + if (auto nextImageRef = getNeighboringImageReference(true)) + switchImageReference(nextImageRef); +} + std::shared_ptr<Label> ProjectController::createLabel(QString name) const { return callControllerFunction("Creating a new label", [this](QString name) { @@ -289,6 +301,34 @@ void ProjectController::updateCurrentProjectData() _currentProjectSettings = serializer.serializeProject(); } +ImageReference* ProjectController::getNeighboringImageReference(bool getPrevious) const +{ + // Copy and sort all image references + auto imageRefs = _project->imageReferences(); + std::sort(imageRefs.begin(), imageRefs.end(), [](const auto& imageRef1, const auto& imageRef2) { return *imageRef1 < *imageRef2; }); + + // Get the next/previous image reference, wrapping around if necessary + auto index = imageRefs.indexOf(_activeImageReference); + + if (index != -1) + { + if (getPrevious) + { + if (--index < 0) + index = static_cast<int>(imageRefs.size()) - 1; + } + else + { + if (++index >= static_cast<int>(imageRefs.size())) + index = 0; + } + + return imageRefs[index].get(); + } + else + return nullptr; +} + void ProjectController::labelCreated(const std::shared_ptr<Label>& label) const { if (_labelsList) diff --git a/Grinder/controller/ProjectController.h b/Grinder/controller/ProjectController.h index e3cad83cd2f01ed3c3e04150b648ddf8f1bc110e..e8ed3e5054e27a6b109138a22605262e2aad15df 100644 --- a/Grinder/controller/ProjectController.h +++ b/Grinder/controller/ProjectController.h @@ -9,7 +9,7 @@ #include <memory> #include "GenericController.h" -#include "project/serialization/SettingsContainer.h" +#include "common/serialization/SettingsContainer.h" namespace grndr { @@ -44,6 +44,8 @@ namespace grndr const Label* activeLabel() const { return _activeLabel; } void switchImageReference(ImageReference* imageRef); + void switchToNextImageReference(); + void switchToPreviousImageReference(); ImageReference* activeImageReference() { return _activeImageReference; } const ImageReference* activeImageReference() const { return _activeImageReference; } @@ -79,6 +81,9 @@ namespace grndr void setCurrentProjectFile(QString fileName); void updateCurrentProjectData(); + private: + ImageReference* getNeighboringImageReference(bool getPrevious) const; + private slots: void labelCreated(const std::shared_ptr<Label>& label) const; void labelRemoved(const std::shared_ptr<Label>& label) const; diff --git a/Grinder/core/GrinderApplication.cpp b/Grinder/core/GrinderApplication.cpp index 9099ea5a4422239f344576406ed7b67c5e66725f..0239d8254dcdc7dd26069efc585a95149e8c01a8 100644 --- a/Grinder/core/GrinderApplication.cpp +++ b/Grinder/core/GrinderApplication.cpp @@ -25,7 +25,7 @@ QFont GrinderApplication::boldFont(QWidget* widget) } GrinderApplication::GrinderApplication(int& argc, char** argv, int flags) : QApplication(argc, argv, flags), - _settings{GRNDR_INFO_COMPANY, GRNDR_INFO_TITLE}, _projectController{&_project}, _engineController{&_engine}, _imageEditorManager{&_pipelineManager} + _settings{GRNDR_INFO_COMPANY, GRNDR_INFO_TITLE}, _projectController{&_project}, _engineController{&_engine}, _imageEditorManager{&_project, &_pipelineManager} { s_appInstance = this; diff --git a/Grinder/core/GrinderApplication.h b/Grinder/core/GrinderApplication.h index 0ecb9519cfadfb0e966baf0bfa21f3e683be240d..e18922826be2cc5d6bc4e06983586b056187f48e 100644 --- a/Grinder/core/GrinderApplication.h +++ b/Grinder/core/GrinderApplication.h @@ -9,6 +9,7 @@ #include <QApplication> #include "GrinderSettings.h" +#include "ClipboardManager.h" #include "pipeline/PipelineManager.h" #include "project/Project.h" #include "engine/Engine.h" @@ -41,6 +42,8 @@ namespace grndr public: GrinderSettings& settings() { return _settings; } const GrinderSettings& settings() const { return _settings; } + ClipboardManager& clipboardManager() { return _clipboardManager; } + const ClipboardManager& clipboardManager() const { return _clipboardManager; } PipelineManager& pipelineManager() { return _pipelineManager; } const PipelineManager& pipelineManager() const { return _pipelineManager; } @@ -68,6 +71,7 @@ namespace grndr private: GrinderSettings _settings; + ClipboardManager _clipboardManager; PipelineManager _pipelineManager; PipelineController _pipelineController; diff --git a/Grinder/image/DraftItem.cpp b/Grinder/image/DraftItem.cpp index 73e8eea6c5f5be7142cca41b9dd9e4dbbd720cd5..47dd8190f17b0e858de367e57f4cba2028efc08f 100644 --- a/Grinder/image/DraftItem.cpp +++ b/Grinder/image/DraftItem.cpp @@ -6,7 +6,6 @@ #include "Grinder.h" #include "DraftItem.h" #include "Layer.h" -#include "common/properties/RangeConstraint.h" const char* DraftItem::Serialization_Value_Type = "Type"; diff --git a/Grinder/image/DraftItem.h b/Grinder/image/DraftItem.h index 5ac87df4060fe3f3171375602e60f6c588f44dd6..a16ab4ff76586c90490f44cf2249d445cf1e02c8 100644 --- a/Grinder/image/DraftItem.h +++ b/Grinder/image/DraftItem.h @@ -6,7 +6,7 @@ #ifndef DRAFTITEM_H #define DRAFTITEM_H -#include "common/PropertyObject.h" +#include "common/properties/PropertyObject.h" #include "DraftItemType.h" #include "DraftItemRendererBase.h" diff --git a/Grinder/image/ImageBuild.cpp b/Grinder/image/ImageBuild.cpp index a035a70c6fcac9a77c69f01f83eb930af6f1a0c4..a2061eb361c7094d33b675a6330256dc8fb53cf2 100644 --- a/Grinder/image/ImageBuild.cpp +++ b/Grinder/image/ImageBuild.cpp @@ -54,6 +54,14 @@ void ImageBuild::removeLayer(const Layer* layer) } } +void ImageBuild::removeAllLayers() +{ + auto layers = _layers; + + for (const auto& layer : layers) + removeLayer(layer.get()); +} + void ImageBuild::moveLayer(const Layer* layer, bool up) { int index = _layers.indexOf(layer); @@ -80,7 +88,8 @@ void ImageBuild::moveLayer(const Layer* layer, bool up) void ImageBuild::serialize(SerializationContext& ctx) const { // Serialize values - ctx.settings()[Serialization_Value_ImageReference] = ctx.getImageReferenceIndex(_imageReference); + if (ctx.getMode() == SerializationContext::Mode::ProjectSerialization) + ctx.settings()[Serialization_Value_ImageReference] = ctx.getImageReferenceIndex(_imageReference); // Serialize all layers ctx.beginGroup(LayerVector::Serialization_Group, true); @@ -91,12 +100,15 @@ void ImageBuild::serialize(SerializationContext& ctx) const void ImageBuild::deserialize(DeserializationContext& ctx) { // Deserialize values - int imageRefIndex = ctx.settings()[Serialization_Value_ImageReference].toInt(); - - if (imageRefIndex != -1) + if (ctx.getMode() == SerializationContext::Mode::ProjectSerialization) { - if (auto imageRef = ctx.getImageReference(imageRefIndex)) - _imageReference = imageRef; + int imageRefIndex = ctx.settings()[Serialization_Value_ImageReference].toInt(); + + if (imageRefIndex != -1) + { + if (auto imageRef = ctx.getImageReference(imageRefIndex)) + _imageReference = imageRef; + } } // Deserialize all layers diff --git a/Grinder/image/ImageBuild.h b/Grinder/image/ImageBuild.h index 4d6ac052255a850cadd6f51524c4eaf082738a58..16dcf034fa39424d657164dfa7082aeb3698a0ef 100644 --- a/Grinder/image/ImageBuild.h +++ b/Grinder/image/ImageBuild.h @@ -27,6 +27,7 @@ namespace grndr public: std::shared_ptr<Layer> createLayer(QString name = ""); void removeLayer(const Layer* layer); + void removeAllLayers(); void moveLayer(const Layer* layer, bool up); diff --git a/Grinder/image/ImageBuildItem.h b/Grinder/image/ImageBuildItem.h index 91b224b608e485ead3b66f8b376136d1773d1d35..b5a3bda8821b379d35847064595163e18d61bc75 100644 --- a/Grinder/image/ImageBuildItem.h +++ b/Grinder/image/ImageBuildItem.h @@ -8,8 +8,8 @@ #include <QObject> -#include "project/serialization/SerializationContext.h" -#include "project/serialization/DeserializationContext.h" +#include "common/serialization/SerializationContext.h" +#include "common/serialization/DeserializationContext.h" namespace grndr { diff --git a/Grinder/image/draftitems/BoxDraftItem.cpp b/Grinder/image/draftitems/BoxDraftItem.cpp index 9b09637196ccfa66577de5e7c1674fe5982e6331..dde2b2a78992c329a9bd851b6bb8090cff28895a 100644 --- a/Grinder/image/draftitems/BoxDraftItem.cpp +++ b/Grinder/image/draftitems/BoxDraftItem.cpp @@ -6,7 +6,6 @@ #include "Grinder.h" #include "BoxDraftItem.h" #include "BoxDraftItemRenderer.h" -#include "common/properties/RangeConstraint.h" const DraftItemType BoxDraftItem::type_value = DraftItemType::Box; diff --git a/Grinder/image/draftitems/LineDraftItem.cpp b/Grinder/image/draftitems/LineDraftItem.cpp index d1689263df7fb44d056c97bfa10a51eb8df91eed..5d906b2dd8f95c16473ae2082f69d2431c61d61b 100644 --- a/Grinder/image/draftitems/LineDraftItem.cpp +++ b/Grinder/image/draftitems/LineDraftItem.cpp @@ -6,7 +6,6 @@ #include "Grinder.h" #include "LineDraftItem.h" #include "LineDraftItemRenderer.h" -#include "common/properties/RangeConstraint.h" const DraftItemType LineDraftItem::type_value = DraftItemType::Line; diff --git a/Grinder/pipeline/PipelineItem.h b/Grinder/pipeline/PipelineItem.h index 61e1bff1c5a922926c06d1900b4d85a4870d9235..b65fb1fc8ddffb7d481808d0c33f64de4f436f70 100644 --- a/Grinder/pipeline/PipelineItem.h +++ b/Grinder/pipeline/PipelineItem.h @@ -6,7 +6,7 @@ #ifndef PIPELINEITEM_H #define PIPELINEITEM_H -#include "common/PropertyObject.h" +#include "common/properties/PropertyObject.h" namespace grndr { diff --git a/Grinder/pipeline/blocks/BinaryThresholdBlock.cpp b/Grinder/pipeline/blocks/BinaryThresholdBlock.cpp index 073a3c35357cb7203127f0112dd38e1f8c70f166..878b70bea2f3092805018ef066150d24667364cb 100644 --- a/Grinder/pipeline/blocks/BinaryThresholdBlock.cpp +++ b/Grinder/pipeline/blocks/BinaryThresholdBlock.cpp @@ -5,7 +5,6 @@ #include "Grinder.h" #include "BinaryThresholdBlock.h" -#include "common/properties/RangeConstraint.h" #include "engine/processors/BinaryThresholdProcessor.h" const BlockType BinaryThresholdBlock::type_value = BlockType::BinaryThreshold; diff --git a/Grinder/project/ImageReference.h b/Grinder/project/ImageReference.h index 6c0f575ad313e9f7ee4ffab56cd178411eb98654..57d29e584223194dce077811e8a3d885de45f130 100644 --- a/Grinder/project/ImageReference.h +++ b/Grinder/project/ImageReference.h @@ -11,8 +11,8 @@ #include <opencv2/core.hpp> #include "project/ProjectItem.h" -#include "project/serialization/SerializationContext.h" -#include "project/serialization/DeserializationContext.h" +#include "common/serialization/SerializationContext.h" +#include "common/serialization/DeserializationContext.h" namespace grndr { diff --git a/Grinder/project/Project.h b/Grinder/project/Project.h index b8b7fb5c04dfec56e57f97e2cc9763e1fce4165c..0d655b192ec9ea4602bbdaf7c9e1844b8e4805e8 100644 --- a/Grinder/project/Project.h +++ b/Grinder/project/Project.h @@ -10,8 +10,8 @@ #include "LabelVector.h" #include "ImageReferenceVector.h" -#include "serialization/SerializationContext.h" -#include "serialization/DeserializationContext.h" +#include "common/serialization/SerializationContext.h" +#include "common/serialization/DeserializationContext.h" namespace grndr { diff --git a/Grinder/res/Grinder.qrc b/Grinder/res/Grinder.qrc index 65b460948d7de489c5e8f7f54742b75a62ca81a3..3717e2047e7fcd750dfae9581166bee64f01f7cb 100644 --- a/Grinder/res/Grinder.qrc +++ b/Grinder/res/Grinder.qrc @@ -35,6 +35,10 @@ <file>icons/drag.png</file> <file>icons/arrowheads-of-thin-outline-to-the-left.png</file> <file>icons/right-thin-arrowheads.png</file> + <file>icons/clipboard-paste-option.png</file> + <file>icons/cut.png</file> + <file>icons/copy-documents-option.png</file> + <file>icons/documents-exchange-2.png</file> </qresource> <qresource prefix="/"> <file>css/global.css</file> diff --git a/Grinder/res/Resources.h b/Grinder/res/Resources.h index 6c083f38bde30d4d00355a9f241f7ac8732edb74..aa6db3d6c3cc28e0284490e201ff7542735e6f5d 100644 --- a/Grinder/res/Resources.h +++ b/Grinder/res/Resources.h @@ -29,6 +29,9 @@ #define FILE_ICON_DELETE ":/icons/icons/delete.png" #define FILE_ICON_DELETE_SELECTED ":/icons/icons/delete-sel-items.png" #define FILE_ICON_SELECTALL ":/icons/icons/select-all.png" +#define FILE_ICON_COPY ":/icons/icons/copy-documents-option.png" +#define FILE_ICON_PASTE ":/icons/icons/clipboard-paste-option.png" +#define FILE_ICON_CUT ":/icons/icons/cut.png" #define FILE_ICON_MOVEUP ":/icons/icons/arrow-up.png" #define FILE_ICON_MOVEDOWN ":/icons/icons/arrow-down.png" @@ -50,6 +53,7 @@ #define FILE_ICON_EDITOR_COLORPICKER ":/icons/icons/painting/eyedropper.png" #define FILE_ICON_EDITOR_SHOWDIRECTIONS ":/icons/icons/show-arrows.png" #define FILE_ICON_EDITOR_SHOWTAGS ":/icons/icons/show-tags.png" +#define FILE_ICON_EDITOR_COPYFROMPREVIOUS ":/icons/icons/documents-exchange-2.png" /* Cursors */ diff --git a/Grinder/ui/graph/GraphBlockNode.cpp b/Grinder/ui/graph/GraphBlockNode.cpp index 207defbab22c653fcfba7e397c5dd1fb4d59cbb0..a3a19d6ca7df00e8aab343b5e33c01e2a90f70c9 100644 --- a/Grinder/ui/graph/GraphBlockNode.cpp +++ b/Grinder/ui/graph/GraphBlockNode.cpp @@ -39,8 +39,8 @@ GraphBlockNode::GraphBlockNode(grndr::GraphScene* scene, const std::shared_ptr<B _nameEditWidget->setWidget(_nameEdit); // Create node actions - _renameAction = createNodeAction("&Rename block", FILE_ICON_EDIT, SLOT(beginRenameBlock()), "Rename the current block", "F2"); - _deleteAction->setText("&Delete block"); + _renameAction = createNodeAction("&Rename block", FILE_ICON_EDIT, SLOT(beginRenameBlock()), "Rename the selected block", "F2"); + _deleteAction->setText("&Delete block(s)"); // Set the tooltip to the block's description QString description = BlockCatalog::getDescription(block->getType()); diff --git a/Grinder/ui/graph/GraphConnectionNode.cpp b/Grinder/ui/graph/GraphConnectionNode.cpp index 859e4ed1f1653a2b8b0db7c913bda2013c882313..f4e903ff25c79db844c701318f4f8dda32afa877 100644 --- a/Grinder/ui/graph/GraphConnectionNode.cpp +++ b/Grinder/ui/graph/GraphConnectionNode.cpp @@ -23,7 +23,7 @@ GraphConnectionNode::GraphConnectionNode(GraphScene* scene, const std::shared_pt updateGeometry(); // Create node actions - _deleteAction->setText("&Remove connection"); + _deleteAction->setText("&Remove connection(s)"); // Set the tooltip showing information about this connection updateToolTip(); diff --git a/Grinder/ui/graph/GraphLayout.h b/Grinder/ui/graph/GraphLayout.h index 8a612ca174a7aefea5bf767566767d044686beba..5a940f9361aa75ebfd6a2e74e6877aaf126da204 100644 --- a/Grinder/ui/graph/GraphLayout.h +++ b/Grinder/ui/graph/GraphLayout.h @@ -11,8 +11,8 @@ #include <memory> #include "pipeline/BlockHierarchy.h" -#include "project/serialization/SerializationContext.h" -#include "project/serialization/DeserializationContext.h" +#include "common/serialization/SerializationContext.h" +#include "common/serialization/DeserializationContext.h" namespace grndr { diff --git a/Grinder/ui/graph/GraphNode.cpp b/Grinder/ui/graph/GraphNode.cpp index 6ea31edac0cfd7bc6cc656c6c1da6488e2788bac..ce6eae34af94c5b16d34e89651ea2fd6a4a2318f 100644 --- a/Grinder/ui/graph/GraphNode.cpp +++ b/Grinder/ui/graph/GraphNode.cpp @@ -18,16 +18,7 @@ GraphNode::GraphNode(GraphScene* scene, QGraphicsItem* parent) : VisualNode(scen _scene{scene}, _style{scene->view()->sceneStyle()} { // Create node actions - _deleteAction = createNodeAction("&Delete", FILE_ICON_DELETE, SLOT(deleteNode()), "Remove the selected item", "Del"); -} - -std::vector<QAction*> GraphNode::getNodesActions(QMenu& menu) const -{ - // If multiple items are selected, only show a "delete all" action - auto action = UIUtils::createAction(&menu, "&Delete selected items", FILE_ICON_DELETE_SELECTED, nullptr, "Delete the selected items", "Del"); - action->connect(action, &QAction::triggered, _scene->view(), &GraphView::removeSelectedItems); - - return {action}; + _deleteAction = createNodeAction("&Delete item(s)", FILE_ICON_DELETE, SLOT(deleteNode()), "Remove the selected items", "Del"); } void GraphNode::deleteNode() diff --git a/Grinder/ui/graph/GraphNode.h b/Grinder/ui/graph/GraphNode.h index fa1646fcdce9c0e19d2cd63a4ba8363b0ce00008..15834ee305557e9e502fde01de01950f85a8f892 100644 --- a/Grinder/ui/graph/GraphNode.h +++ b/Grinder/ui/graph/GraphNode.h @@ -22,7 +22,7 @@ namespace grndr protected: virtual std::vector<QAction*> getNodeActions(QMenu& menu) const override { Q_UNUSED(menu); return {nullptr, _deleteAction}; } - virtual std::vector<QAction*> getNodesActions(QMenu& menu) const override; + virtual std::vector<QAction*> getNodesActions(QMenu& menu) const override { Q_UNUSED(menu); return {_deleteAction}; } protected slots: void deleteNode(); diff --git a/Grinder/ui/image/DraftItemNode.cpp b/Grinder/ui/image/DraftItemNode.cpp index 9ef754fb7b4693b49b2bbf21ff1bb274cca8e403..71a823014def470491a5e02e3aa8173e07ad29ba 100644 --- a/Grinder/ui/image/DraftItemNode.cpp +++ b/Grinder/ui/image/DraftItemNode.cpp @@ -33,7 +33,7 @@ DraftItemNode::DraftItemNode(ImageEditorScene* scene, const std::shared_ptr<Draf updateVisibility(); // Create node actions - _deleteAction = createNodeAction("&Delete", FILE_ICON_DELETE, SLOT(deleteNode()), "Remove the selected item", "Del"); + _deleteAction = createNodeAction("&Delete item(s)", FILE_ICON_DELETE, SLOT(deleteNode()), "Remove the selected items", "Del"); } void DraftItemNode::initDraftItemNode() @@ -111,8 +111,15 @@ void DraftItemNode::updateNode() void DraftItemNode::updateZOrder() { - if (auto draftItem = _draftItem.lock()) // Make sure that the underlying draft item still exists - setZValue(draftItem->getZOrder()); // Use the z-order of the draft item + if (isSelected()) // If selected, show on top of all other items (makes in-place editing easier) + { + setZValue(std::numeric_limits<qreal>::max()); + } + else + { + if (auto draftItem = _draftItem.lock()) // Make sure that the underlying draft item still exists + setZValue(draftItem->getZOrder()); // Use the z-order of the draft item + } } void DraftItemNode::updateVisibility() @@ -138,6 +145,8 @@ QVariant DraftItemNode::itemChange(QGraphicsItem::GraphicsItemChange change, con // Show the in-place editor if the item is currently selected if (_inPlaceEditor) _inPlaceEditor->setVisible(value.toBool()); + + updateZOrder(); } return ImageEditorNode::itemChange(change, value); @@ -151,15 +160,6 @@ void DraftItemNode::updateGeometry() ImageEditorNode::updateGeometry(); } -std::vector<QAction*> DraftItemNode::getNodesActions(QMenu& menu) const -{ - // If multiple items are selected, only show a "delete all" action - auto action = UIUtils::createAction(&menu, "&Delete selected items", FILE_ICON_DELETE_SELECTED, nullptr, "Delete the selected items", "Del"); - action->connect(action, &QAction::triggered, _scene->view(), &ImageEditorView::removeSelectedItems); - - return {action}; -} - void DraftItemNode::deleteNode() { _imageEditor->controller().removeSelectedNodes(); diff --git a/Grinder/ui/image/DraftItemNode.h b/Grinder/ui/image/DraftItemNode.h index e331806a0f81adf197faf397bb64cdc335d72ee4..b59cab44c6dc584269be9923dc48519fca62ffc4 100644 --- a/Grinder/ui/image/DraftItemNode.h +++ b/Grinder/ui/image/DraftItemNode.h @@ -50,7 +50,7 @@ namespace grndr virtual void updateGeometry() override; virtual std::vector<QAction*> getNodeActions(QMenu& menu) const override { Q_UNUSED(menu); return {nullptr, _deleteAction}; } - virtual std::vector<QAction*> getNodesActions(QMenu& menu) const override; + virtual std::vector<QAction*> getNodesActions(QMenu& menu) const override { Q_UNUSED(menu); return {nullptr, _deleteAction}; } protected slots: void deleteNode(); diff --git a/Grinder/ui/image/ImageEditorDockWidget.cpp b/Grinder/ui/image/ImageEditorDockWidget.cpp index 456fe4110e77e5e0a9fafc5425ab6e6ed3298967..ea62c751c08cecdda7cfe13ea2645a897f7bfc2e 100644 --- a/Grinder/ui/image/ImageEditorDockWidget.cpp +++ b/Grinder/ui/image/ImageEditorDockWidget.cpp @@ -5,7 +5,7 @@ #include "Grinder.h" #include "ImageEditorDockWidget.h" -#include "ImageEditorWidget.h" +#include "ImageEditorView.h" #include "core/GrinderApplication.h" #include "ui/StyleSheet.h" #include "res/Resources.h" @@ -32,8 +32,8 @@ void ImageEditorDockWidget::dockShown(bool shown) { if (shown) { - if (auto editorWidget = findChild<ImageEditorWidget*>()) - editorWidget->setFocus(); + if (auto editorView= findChild<ImageEditorView*>()) + editorView->setFocus(); } } diff --git a/Grinder/ui/image/ImageEditorManager.cpp b/Grinder/ui/image/ImageEditorManager.cpp index 0a8646c5e4ed20ae4f75b53ab9c0ddc7a7b4d9be..b3e6b6ced6a49ebecf1ab569a957e18dd3053525 100644 --- a/Grinder/ui/image/ImageEditorManager.cpp +++ b/Grinder/ui/image/ImageEditorManager.cpp @@ -10,9 +10,11 @@ #include "image/ImageExceptions.h" #include "pipeline/Pipeline.h" -ImageEditorManager::ImageEditorManager(PipelineManager* pipelineManager) : QObject(pipelineManager) +ImageEditorManager::ImageEditorManager(Project* project, PipelineManager* pipelineManager) : QObject(pipelineManager) { + // We need to close editors when an image reference or an entire pipeline is removed connect(pipelineManager, &PipelineManager::pipelineRemoved, this, &ImageEditorManager::pipelineRemoved); + connect(project, &Project::imageReferenceRemoved, this, &ImageEditorManager::imageReferenceRemoved); } void ImageEditorManager::showEditor(const Block* block, const std::shared_ptr<grndr::ImageBuild>& imageBuild) @@ -117,6 +119,21 @@ void ImageEditorManager::blockRemoved(const std::shared_ptr<Block>& block) closeEditor(block.get()); } +void ImageEditorManager::imageReferenceRemoved(const std::shared_ptr<ImageReference>& imageReference) +{ + // Remove all editors that are currently showing the removed image reference + std::vector<const Block*> editorsToRemove; + + for (const auto& editor : _editors) + { + if (editor.second->controller().activeImageBuild()->imageReference() == imageReference.get()) + editorsToRemove.push_back(editor.first); + } + + for (const auto& editorBlock : editorsToRemove) + closeEditor(editorBlock); +} + void ImageEditorManager::updateDockNames() { // Update all dock names diff --git a/Grinder/ui/image/ImageEditorManager.h b/Grinder/ui/image/ImageEditorManager.h index 52a645fadfcac774b817dcdc1a3a6ab126e81b9e..11922ddfde226493e80f077b4d203c2f5aa2ff36 100644 --- a/Grinder/ui/image/ImageEditorManager.h +++ b/Grinder/ui/image/ImageEditorManager.h @@ -21,7 +21,7 @@ namespace grndr Q_OBJECT public: - ImageEditorManager(PipelineManager* pipelineManager); + ImageEditorManager(Project* project, PipelineManager* pipelineManager); public: void showEditor(const Block* block, const std::shared_ptr<ImageBuild>& imageBuild); @@ -42,6 +42,7 @@ namespace grndr void pipelineRemoved(const std::shared_ptr<Pipeline>& pipeline); void blockRemoved(const std::shared_ptr<Block>& block); + void imageReferenceRemoved(const std::shared_ptr<ImageReference>& imageReference); void updateDockNames(); diff --git a/Grinder/ui/image/ImageEditorPropertyWidget.h b/Grinder/ui/image/ImageEditorPropertyWidget.h index f2e772455234f2b4ba37376cf2b2e0afce76abf3..d3d274eadc2961498e5065101788ffa577602200 100644 --- a/Grinder/ui/image/ImageEditorPropertyWidget.h +++ b/Grinder/ui/image/ImageEditorPropertyWidget.h @@ -10,7 +10,7 @@ #include <QGridLayout> #include "ImageEditorComponent.h" -#include "common/PropertyVector.h" +#include "common/properties/PropertyVector.h" namespace grndr { diff --git a/Grinder/ui/image/ImageEditorTool.h b/Grinder/ui/image/ImageEditorTool.h index 845ee1087b5eced3c44241494a08c0e80ba8cc3e..80631a5f4f76033cb0f51f3b53c8871a526557a0 100644 --- a/Grinder/ui/image/ImageEditorTool.h +++ b/Grinder/ui/image/ImageEditorTool.h @@ -10,7 +10,7 @@ #include <QCursor> #include <QKeySequence> -#include "common/PropertyObject.h" +#include "common/properties/PropertyObject.h" #include "ui/visscene/VisualSceneInputHandler.h" #include "ImageEditorComponent.h" diff --git a/Grinder/ui/image/ImageEditorView.cpp b/Grinder/ui/image/ImageEditorView.cpp index 1b13ef3fae4a08e5453b3c08f519db394a26b017..3fab8df06033ce92560e31a9c8b335e4f806a8f9 100644 --- a/Grinder/ui/image/ImageEditorView.cpp +++ b/Grinder/ui/image/ImageEditorView.cpp @@ -7,6 +7,7 @@ #include "ImageEditorView.h" #include "ImageEditorScene.h" #include "ImageEditor.h" +#include "core/GrinderApplication.h" #include "controller/ImageEditorController.h" #include "util/UIUtils.h" #include "res/Resources.h" @@ -23,13 +24,21 @@ ImageEditorView::ImageEditorView(QWidget* parent) : VisualSceneView(parent) _showDirectionsAction = UIUtils::createAction(this, "&Show direction arrows", FILE_ICON_EDITOR_SHOWDIRECTIONS, SLOT(showDirectionArrows()), "Show direction arrows on items"); _showDirectionsAction->setCheckable(true); _showDirectionsAction->setChecked(true); - _showTagsAction = UIUtils::createAction(this, "&Show tags", FILE_ICON_EDITOR_SHOWTAGS, SLOT(showTags()), "Show tags on items"); _showTagsAction->setCheckable(true); - _showTagsAction->setChecked(true); - + _showTagsAction->setChecked(true); _zoomFitAction = UIUtils::createAction(this, "Zoom to &window", FILE_ICON_ZOOMFIT, SLOT(fitImageToWindow()), "Zoom the view to fit the image to its window", "Ctrl+Alt+A"); + _copyDraftItems = UIUtils::createAction(this, "&Copy item(s)", FILE_ICON_COPY, SLOT(copyDraftItems()), "Copy the selected items to the clipboard"); + _copyDraftItems->setShortcut(QKeySequence{QKeySequence::Copy}); + _pasteDraftItems = UIUtils::createAction(this, "&Paste item(s)", FILE_ICON_PASTE, SLOT(pasteDraftItems()), "Paste items from the clipboard"); + _pasteDraftItems->setShortcut(QKeySequence{QKeySequence::Paste}); + _cutDraftItems = UIUtils::createAction(this, "Cu&t item(s)", FILE_ICON_CUT, SLOT(cutDraftItems()), "Copy the selected items to the clipboard and remove them afterwards"); + _cutDraftItems->setShortcut(QKeySequence{QKeySequence::Cut}); + + // Listen for clipboard changes to update our actions + connect(&grinder()->clipboardManager(), &ClipboardManager::dataChanged, this, &ImageEditorView::updateActions); + updateActions(); } @@ -62,12 +71,31 @@ void ImageEditorView::removeSelectedItems() const _editorScene->imageEditor()->controller().removeSelectedNodes(); } +void ImageEditorView::prepareNodeContextMenu(QMenu& menu) const +{ + QAction* firstAction = !menu.actions().isEmpty() ? menu.actions().first() : nullptr; + + menu.insertAction(firstAction, _cutDraftItems); + menu.insertAction(firstAction, _copyDraftItems); + menu.insertAction(firstAction, _pasteDraftItems); +} + +void ImageEditorView::prepareNodesContextMenu(QMenu& menu) const +{ + prepareNodeContextMenu(menu); +} + std::vector<QAction*> ImageEditorView::getActions(AddActionsMode mode) const { std::vector<QAction*> actions; if (mode != AddActionsMode::Toolbar) { + actions.push_back(_cutDraftItems); + actions.push_back(_copyDraftItems); + actions.push_back(_pasteDraftItems); + actions.push_back(nullptr); + actions.push_back(_selectAllAction); actions.push_back(_deleteSelectedAction); actions.push_back(nullptr); @@ -102,6 +130,10 @@ void ImageEditorView::updateActions() VisualSceneView::updateActions(); _zoomFitAction->setEnabled(_scene); + + _copyDraftItems->setEnabled(_scene && !_scene->selectedItems().isEmpty()); + _pasteDraftItems->setEnabled(_scene && grinder()->clipboardManager().hasData(DraftItemVector::Serialization_Element)); + _cutDraftItems->setEnabled(_scene && !_scene->selectedItems().isEmpty()); } void ImageEditorView::showDirectionArrows() @@ -149,3 +181,21 @@ void ImageEditorView::showTagsChanged(bool show) _showTagsAction->setChecked(show); update(); } + +void ImageEditorView::copyDraftItems() const +{ + if (_editorScene) + _editorScene->imageEditor()->controller().copySelectedNodes(); +} + +void ImageEditorView::pasteDraftItems() const +{ + if (_editorScene) + _editorScene->imageEditor()->controller().pasteSelectedNodes(); +} + +void ImageEditorView::cutDraftItems() const +{ + if (_editorScene) + _editorScene->imageEditor()->controller().cutSelectedNodes(); +} diff --git a/Grinder/ui/image/ImageEditorView.h b/Grinder/ui/image/ImageEditorView.h index 57f312e09276b0b087106e085e3df1c61528979a..45a32ccc165bbe5993c5e26f20e4e75b6b9d63df 100644 --- a/Grinder/ui/image/ImageEditorView.h +++ b/Grinder/ui/image/ImageEditorView.h @@ -32,6 +32,9 @@ namespace grndr public: virtual const ImageEditorStyle& sceneStyle() const override { return _editorStyle; } + virtual void prepareNodeContextMenu(QMenu& menu) const override; + virtual void prepareNodesContextMenu(QMenu& menu) const override; + protected: virtual std::vector<QAction*> getActions(AddActionsMode mode) const override; @@ -51,6 +54,10 @@ namespace grndr void showDirectionArrowsChanged(bool show); void showTagsChanged(bool show); + void copyDraftItems() const; + void pasteDraftItems() const; + void cutDraftItems() const; + private: ImageEditorScene* _editorScene{nullptr}; @@ -60,6 +67,10 @@ namespace grndr QAction* _showDirectionsAction{nullptr}; QAction* _showTagsAction{nullptr}; QAction* _zoomFitAction{nullptr}; + + QAction* _copyDraftItems{nullptr}; + QAction* _pasteDraftItems{nullptr}; + QAction* _cutDraftItems{nullptr}; }; } diff --git a/Grinder/ui/image/ImageEditorWidget.cpp b/Grinder/ui/image/ImageEditorWidget.cpp index 39b3697e292caa3ffcbd80a35845d394584d8b81..f43f7a66e2b6d28a798df564729e6346cb622bfd 100644 --- a/Grinder/ui/image/ImageEditorWidget.cpp +++ b/Grinder/ui/image/ImageEditorWidget.cpp @@ -14,16 +14,32 @@ #include "util/UIUtils.h" #include "res/Resources.h" +#include <QMetaType> +Q_DECLARE_METATYPE(std::shared_ptr<ImageReference>) + ImageEditorWidget::ImageEditorWidget(ImageEditor* imageEditor, QWidget* parent) : QWidget(parent), ImageEditorComponent(imageEditor), ui{new Ui::ImageEditorWidget} { if (!imageEditor) throw std::runtime_error{_EXCPT("imageEditor may not be null")}; - setupUi(); + // Needed to be able to queue imageReferenceRemoved signals + qRegisterMetaType<std::shared_ptr<ImageReference>>(); + + // Create editor actions + _copyImageBuild = UIUtils::createAction(this, "&Copy image build", FILE_ICON_COPY, SLOT(copyImageBuild()), "Copy the current image build to the clipboard (Ctrl+Shift+C)", "Ctrl+Shift+C", Qt::WidgetWithChildrenShortcut); + _pasteImageBuild = UIUtils::createAction(this, "&Paste image build", FILE_ICON_PASTE, SLOT(pasteImageBuild()), "Paste an image build from the clipboard (Ctrl+Shift+V)", "Ctrl+Shift+V", Qt::WidgetWithChildrenShortcut); + _duplicateImageBuild = UIUtils::createAction(this, "&Copy the current image build to the next image", FILE_ICON_EDITOR_COPYFROMPREVIOUS, SLOT(duplicateImageBuild()), "Copy the current image build to the next image (Ctrl+D)", "Ctrl+D", Qt::WidgetWithChildrenShortcut); + + setupUi(); // Reflect primary color changes connect(&_imageEditor->environment(), &ImageEditorEnvironment::primaryColorChanged, this, &ImageEditorWidget::primaryColorChanged); + + // Listen for various events to update our actions + connect(&grinder()->project(), SIGNAL(imageReferenceCreated(const std::shared_ptr<ImageReference>&)), this, SLOT(updateActions())); + connect(&grinder()->project(), SIGNAL(imageReferenceRemoved(const std::shared_ptr<ImageReference>&)), this, SLOT(updateActions()), Qt::QueuedConnection); // Delay this signal so that the image reference has been removed from the image references list + connect(&grinder()->clipboardManager(), &ClipboardManager::dataChanged, this, &ImageEditorWidget::updateActions); } ImageEditorWidget::~ImageEditorWidget() @@ -36,12 +52,21 @@ void ImageEditorWidget::setImageBuild(const std::shared_ptr<ImageBuild>& imageBu if (auto scene = _imageEditor->controller().activeScene()) disconnect(scene, nullptr, this, nullptr); + if (auto imageBuild = _imageEditor->controller().activeImageBuild()) + disconnect(imageBuild, nullptr, this, nullptr); + // Let the controller handle showing the new image build _imageEditor->controller().showImageBuild(imageBuild.get()); // Listen for node selection changes in the new scene to update the primary color if (auto scene = _imageEditor->controller().activeScene()) connect(scene, &ImageEditorScene::selectionChanged, this, &ImageEditorWidget::updatePrimaryColor); + + // Listen for various events to update our actions + connect(imageBuild.get(), SIGNAL(layerCreated(const std::shared_ptr<Layer>&)), this, SLOT(updateActions())); + connect(imageBuild.get(), SIGNAL(layerRemoved(const std::shared_ptr<Layer>&)), this, SLOT(updateActions())); + + updateActions(); } void ImageEditorWidget::keyPressEvent(QKeyEvent* event) @@ -93,6 +118,8 @@ void ImageEditorWidget::setupUi() grinder()->settings().getSplitterState("ImageEditorLeft", ui->splitterLeft); grinder()->settings().getSplitterState("ImageEditorRight", ui->splitterRight); + + updateActions(); } void ImageEditorWidget::setupImageControlBar() @@ -104,6 +131,12 @@ void ImageEditorWidget::setupImageControlBar() for (auto toolAction : _imageEditor->editorTools().getActions()) ui->imageSceneControlBar->addAction(toolAction, Qt::ToolButtonFollowStyle, Qt::AlignLeft); + // Add the copy&paste actions + ui->imageSceneControlBar->addSeparator(Qt::AlignLeft); + ui->imageSceneControlBar->addAction(_duplicateImageBuild, Qt::ToolButtonFollowStyle, Qt::AlignLeft); + ui->imageSceneControlBar->addAction(_copyImageBuild, Qt::ToolButtonFollowStyle, Qt::AlignLeft); + ui->imageSceneControlBar->addAction(_pasteImageBuild, Qt::ToolButtonFollowStyle, Qt::AlignLeft); + ui->imageScene->setupUi(static_cast<QMenu*>(nullptr), ui->imageSceneControlBar); // Add the zoom level label @@ -155,6 +188,15 @@ void ImageEditorWidget::on_primaryColorWidget_customContextMenuRequested(const Q menu.exec(ui->primaryColorWidget->mapToGlobal(pos)); } +void ImageEditorWidget::updateActions() +{ + auto imageBuild = _imageEditor->controller().activeImageBuild(); + + _copyImageBuild->setEnabled(imageBuild); + _duplicateImageBuild->setEnabled(imageBuild && grinder()->project().imageReferences().size() > 1); + _pasteImageBuild->setEnabled(imageBuild && grinder()->clipboardManager().hasData(ImageBuildVector::Serialization_Element)); +} + void ImageEditorWidget::updateSceneZoomLevel(qreal zoomLevel) { _imageSceneZoomLabel->setText(QString{"%1%"}.arg(static_cast<int>(zoomLevel * 100.0))); @@ -204,6 +246,21 @@ void ImageEditorWidget::assignPrimaryColorToPreset() } } +void ImageEditorWidget::copyImageBuild() const +{ + _imageEditor->controller().copyImageBuild(); +} + +void ImageEditorWidget::pasteImageBuild() const +{ + _imageEditor->controller().pasteImageBuild(); +} + +void ImageEditorWidget::duplicateImageBuild() const +{ + _imageEditor->controller().duplicateImageBuild(); +} + void ImageEditorWidget::primaryColorChanged(QColor color) { ui->primaryColorWidget->setColor(color); diff --git a/Grinder/ui/image/ImageEditorWidget.h b/Grinder/ui/image/ImageEditorWidget.h index 3a50dc88cb1ce52ecc9ebc4a59efe800ea2b22ee..0d6e4dcec9dc9b835ff752693f0a8f6b75a741db 100644 --- a/Grinder/ui/image/ImageEditorWidget.h +++ b/Grinder/ui/image/ImageEditorWidget.h @@ -51,6 +51,7 @@ namespace grndr void on_primaryColorWidget_customContextMenuRequested(const QPoint &pos); private slots: + void updateActions(); void updateSceneZoomLevel(qreal zoomLevel); void updatePrimaryColor(); @@ -59,8 +60,16 @@ namespace grndr void colorPresetSelected(QColor color); void assignPrimaryColorToPreset(); + void copyImageBuild() const; + void pasteImageBuild() const; + void duplicateImageBuild() const; + private: QAction* _newLayerAction{nullptr}; + + QAction* _copyImageBuild{nullptr}; + QAction* _pasteImageBuild{nullptr}; + QAction* _duplicateImageBuild{nullptr}; }; } diff --git a/Grinder/ui/image/LayersListWidget.cpp b/Grinder/ui/image/LayersListWidget.cpp index b7d0962b44a8b8c8dc0934951618db7ba4459b80..790958495aa5c57bd3a386ad71ddadd4be9cad1f 100644 --- a/Grinder/ui/image/LayersListWidget.cpp +++ b/Grinder/ui/image/LayersListWidget.cpp @@ -7,6 +7,7 @@ #include "LayersListWidget.h" #include "ImageEditor.h" #include "image/ImageBuild.h" +#include "core/GrinderApplication.h" #include "ui/widget/ControlBar.h" #include "util/UIUtils.h" #include "util/StringUtils.h" @@ -18,6 +19,12 @@ LayersListWidget::LayersListWidget(QWidget* parent) : MetaWidget(parent) _renameLayerAction = UIUtils::createAction(this, "Rename layer", FILE_ICON_EDIT, SLOT(renameLayer()), "Rename the selected layer", "F2"); _moveUpAction = UIUtils::createAction(this, "Move up", FILE_ICON_MOVEUP, SLOT(moveLayerUp()), "Move the selected layer up", "Ctrl+Shift+Up"); _moveDownAction = UIUtils::createAction(this, "Move down", FILE_ICON_MOVEDOWN, SLOT(moveLayerDown()), "Move the selected layer down", "Ctrl+Shift+Down"); + _copyLayer = UIUtils::createAction(this, "&Copy layer", FILE_ICON_COPY, SLOT(copyLayer()), "Copy the selected layer to the clipboard"); + _copyLayer->setShortcut(QKeySequence{QKeySequence::Copy}); + _pasteLayer = UIUtils::createAction(this, "&Paste layer", FILE_ICON_PASTE, SLOT(pasteLayer()), "Paste layer from the clipboard"); + _pasteLayer->setShortcut(QKeySequence{QKeySequence::Paste}); + _cutLayer = UIUtils::createAction(this, "Cu&t layer", FILE_ICON_CUT, SLOT(cutLayer()), "Copy the selected layer to the clipboard and remove it afterwards"); + _cutLayer->setShortcut(QKeySequence{QKeySequence::Cut}); _removeLayerAction = UIUtils::createAction(this, "&Remove layer", FILE_ICON_DELETE, SLOT(removeLayer()), "Remove the selected layer", "Del"); _removeAllLayersAction = UIUtils::createAction(this, "Remove all layers", "", SLOT(removeAllLayers()), "Remove all layers"); @@ -33,6 +40,9 @@ LayersListWidget::LayersListWidget(QWidget* parent) : MetaWidget(parent) // Listen for item selections in order to update the active layer and actions connect(this, &LayersListWidget::itemSelectionChanged, this, &LayersListWidget::selectedItemChanged); + // Listen for clipboard changes to update our actions + connect(&grinder()->clipboardManager(), &ClipboardManager::dataChanged, this, &LayersListWidget::updateActions); + updateActions(); } @@ -79,6 +89,10 @@ std::vector<QAction*> LayersListWidget::getActions(MetaWidget::AddActionsMode mo { actions.push_back(_renameLayerAction); actions.push_back(nullptr); + actions.push_back(_cutLayer); + actions.push_back(_copyLayer); + actions.push_back(_pasteLayer); + actions.push_back(nullptr); } } @@ -141,6 +155,23 @@ void LayersListWidget::moveLayerDown() _imageEditor->controller().moveLayer(layerSelected->object(), false); } +void LayersListWidget::copyLayer() +{ + if (auto layerSelected = currentObjectItem()) + _imageEditor->controller().copyLayer(layerSelected->object()); +} + +void LayersListWidget::pasteLayer() +{ + _imageEditor->controller().pasteLayer(); +} + +void LayersListWidget::cutLayer() +{ + if (auto layerSelected = currentObjectItem()) + _imageEditor->controller().cutLayer(layerSelected->object()); +} + void LayersListWidget::removeLayer() { if (auto layer = currentObject()) @@ -206,6 +237,9 @@ void LayersListWidget::updateActions() _renameLayerAction->setEnabled(layerSelected); _moveUpAction->setEnabled(layerSelected && currentRow() > 0); _moveDownAction->setEnabled(layerSelected && currentRow() < count() - 1); + _copyLayer->setEnabled(layerSelected); + _pasteLayer->setEnabled(grinder()->clipboardManager().hasData(LayerVector::Serialization_Element)); + _cutLayer->setEnabled(layerSelected); _removeLayerAction->setEnabled(layerSelected); _removeAllLayersAction->setEnabled(count() > 0); } diff --git a/Grinder/ui/image/LayersListWidget.h b/Grinder/ui/image/LayersListWidget.h index 3d5617c941c585e63212033b34fb100916141311..40a98612e914f4bee8c830af8dba34a44a18f8c7 100644 --- a/Grinder/ui/image/LayersListWidget.h +++ b/Grinder/ui/image/LayersListWidget.h @@ -44,6 +44,9 @@ namespace grndr void newLayer(); void moveLayerUp(); void moveLayerDown(); + void copyLayer(); + void pasteLayer(); + void cutLayer(); void removeLayer(); void removeAllLayers(); @@ -61,6 +64,9 @@ namespace grndr QAction* _newLayerAction{nullptr}; QAction* _moveUpAction{nullptr}; QAction* _moveDownAction{nullptr}; + QAction* _copyLayer{nullptr}; + QAction* _pasteLayer{nullptr}; + QAction* _cutLayer{nullptr}; QAction* _removeLayerAction{nullptr}; QAction* _removeAllLayersAction{nullptr}; }; diff --git a/Grinder/ui/image/draftitems/BoxDraftItemNode.cpp b/Grinder/ui/image/draftitems/BoxDraftItemNode.cpp index 8fa9920ddc4540f8521e7e2978450fb7b242885a..649f75259f182612f107fdd04cfb316fe086d6fe 100644 --- a/Grinder/ui/image/draftitems/BoxDraftItemNode.cpp +++ b/Grinder/ui/image/draftitems/BoxDraftItemNode.cpp @@ -29,8 +29,7 @@ QRect BoxDraftItemNode::getBoundingRect() const int lineWidth = *draftItem->lineWidth(); QSize size = *draftItem->boxSize() + QSize{lineWidth, lineWidth}; - int margin = lineWidth * 2; - return QRect{QPoint{-lineWidth / 2, -lineWidth / 2}, size} + QMargins{margin, margin, margin, margin}; + return QRect{QPoint{-lineWidth / 2, -lineWidth / 2}, size}; } else return QRect{}; diff --git a/Grinder/ui/image/draftitems/LineDraftItemNode.cpp b/Grinder/ui/image/draftitems/LineDraftItemNode.cpp index 49376ae477b02f66d989b1dac5a054997da35ec5..6ab59dc3823e4b739c5f70aa71e3c5b6350bc289 100644 --- a/Grinder/ui/image/draftitems/LineDraftItemNode.cpp +++ b/Grinder/ui/image/draftitems/LineDraftItemNode.cpp @@ -32,8 +32,7 @@ QRect LineDraftItemNode::getBoundingRect() const auto width = std::abs(position.x() - endPosition.x()); auto height = std::abs(position.y() - endPosition.y()); - int margin = *draftItem->lineWidth() * 2; - return QRect{QPoint{0, 0}, QSize{width, height}} + QMargins{margin, margin, margin, margin}; + return QRect{QPoint{0, 0}, QSize{width, height}}; } else return QRect{}; diff --git a/Grinder/ui/image/tools/BoxDraftItemTool.cpp b/Grinder/ui/image/tools/BoxDraftItemTool.cpp index 4a52472bb0fe04cf28509e2c01b768deec0a663e..5a0c3efd7af8bb981068a1b14adce44480726b74 100644 --- a/Grinder/ui/image/tools/BoxDraftItemTool.cpp +++ b/Grinder/ui/image/tools/BoxDraftItemTool.cpp @@ -5,7 +5,6 @@ #include "Grinder.h" #include "BoxDraftItemTool.h" -#include "common/properties/RangeConstraint.h" #include "res/Resources.h" const char* BoxDraftItemTool::tool_type = "BoxDraftItemTool"; diff --git a/Grinder/ui/image/tools/DraftItemTool.cpp b/Grinder/ui/image/tools/DraftItemTool.cpp index 52c2eedb969e8dd22a2f50e0b741acd75f20492e..43074b3b5b83815379dc950d77bd3a9f3076008d 100644 --- a/Grinder/ui/image/tools/DraftItemTool.cpp +++ b/Grinder/ui/image/tools/DraftItemTool.cpp @@ -5,7 +5,6 @@ #include "Grinder.h" #include "DraftItemTool.h" -#include "common/properties/RangeConstraint.h" #include "controller/ImageEditorController.h" #include "ui/image/ImageEditor.h" #include "util/MathUtils.h" @@ -21,13 +20,6 @@ DraftItemTool::DraftItemTool(ImageEditor* imageEditor, DraftItemType itemType, Q std::shared_ptr<DraftItem> DraftItemTool::createDraftItem(QPointF pos, bool selectItem, bool setDefaultProperties) { - // If there are no layers, create a default one and activate it - if (!_imageEditor->controller().activeLayer()) - { - auto layer = _imageEditor->controller().createLayer("Default layer"); - _imageEditor->controller().switchLayer(layer.get()); - } - // Create the draft item auto draftItem = _imageEditor->controller().createDraftItem(_itemType); diff --git a/Grinder/ui/image/tools/LineDraftItemTool.cpp b/Grinder/ui/image/tools/LineDraftItemTool.cpp index e90b14fd58d456a92d04a8bb3dba146a4f8f4bb8..e07b2fc39ed8034588bf5c6308de0209f4eccc74 100644 --- a/Grinder/ui/image/tools/LineDraftItemTool.cpp +++ b/Grinder/ui/image/tools/LineDraftItemTool.cpp @@ -5,7 +5,6 @@ #include "Grinder.h" #include "LineDraftItemTool.h" -#include "common/properties/RangeConstraint.h" #include "res/Resources.h" const char* LineDraftItemTool::tool_type = "LineDraftItemTool"; diff --git a/Grinder/ui/mainwnd/GrinderWindow.cpp b/Grinder/ui/mainwnd/GrinderWindow.cpp index 53cd011d6ce81260b65cfd03b2e3fe036023aef7..09331d4efb3905bbb4023a979727da96f41151e8 100644 --- a/Grinder/ui/mainwnd/GrinderWindow.cpp +++ b/Grinder/ui/mainwnd/GrinderWindow.cpp @@ -116,13 +116,13 @@ void GrinderWindow::on_actExecuteLabel_triggered() void GrinderWindow::on_actExecuteNextImage_triggered() { - ui->imageReferencesList->nextImageReference(); + grinder()->projectController().switchToNextImageReference(); on_actExecuteLabel_triggered(); } void GrinderWindow::on_actExecutePreviousImage_triggered() { - ui->imageReferencesList->previousImageReference(); + grinder()->projectController().switchToPreviousImageReference(); on_actExecuteLabel_triggered(); } @@ -240,8 +240,6 @@ bool GrinderWindow::loadProject(QString fileName) { bool result = true; - setEnabled(false); - try { grinder()->projectController().loadProject(fileName); ui->statusBar->showMessage(QString{"Loaded project '%1'"}.arg(fileName)); @@ -252,8 +250,6 @@ bool GrinderWindow::loadProject(QString fileName) result = false; } - setEnabled(true); - return result; } @@ -261,8 +257,6 @@ bool GrinderWindow::saveProject(QString fileName) { bool result = true; - setEnabled(false); - try { grinder()->projectController().saveProject(fileName); ui->statusBar->showMessage(QString{"Saved the current project to '%1'"}.arg(fileName)); @@ -272,8 +266,6 @@ bool GrinderWindow::saveProject(QString fileName) result = false; } - setEnabled(true); - return result; } diff --git a/Grinder/ui/mainwnd/GrinderWindow.ui b/Grinder/ui/mainwnd/GrinderWindow.ui index 151dbb8e43e3703c3762ef44ff488b2ffae03c98..1896b07739778cdcdfe3b30e3b17930a18c2767b 100644 --- a/Grinder/ui/mainwnd/GrinderWindow.ui +++ b/Grinder/ui/mainwnd/GrinderWindow.ui @@ -649,7 +649,7 @@ <customwidget> <class>PropertyTreeWidget</class> <extends>QTreeWidget</extends> - <header>ui/property/PropertyTreeWidget.h</header> + <header>ui/properties/PropertyTreeWidget.h</header> </customwidget> <customwidget> <class>GrinderDockWidget</class> diff --git a/Grinder/ui/mainwnd/ImageReferencesListWidget.cpp b/Grinder/ui/mainwnd/ImageReferencesListWidget.cpp index 8359d9af6bd4539bf2c3fe4ea8947620e1471d62..28c892818f56e858c4d5cb9fdc3a65f5195f1045 100644 --- a/Grinder/ui/mainwnd/ImageReferencesListWidget.cpp +++ b/Grinder/ui/mainwnd/ImageReferencesListWidget.cpp @@ -127,6 +127,16 @@ void ImageReferencesListWidget::dropEvent(QDropEvent* event) event->ignore(); } +void ImageReferencesListWidget::nextImageReference() const +{ + grinder()->projectController().switchToNextImageReference(); +} + +void ImageReferencesListWidget::previousImageReference() const +{ + grinder()->projectController().switchToPreviousImageReference(); +} + void ImageReferencesListWidget::viewImageReference() { if (auto item = currentObjectItem()) @@ -193,24 +203,3 @@ void ImageReferencesListWidget::updateActions() _removeImageReferenceAction->setEnabled(imageSelected); _removeAllImageReferencesAction->setEnabled(count() > 0); } - -void ImageReferencesListWidget::advanceCurrentImageReference(bool goUp) -{ - int index = findObjectItemIndex(activeObjectItem()); - - if (index != -1) - { - if (goUp) - { - if (--index < 0) - index = count() - 1; - } - else - { - if (++index >= count()) - index = 0; - } - - switchToObjectItem(objectItem(index), false); - } -} diff --git a/Grinder/ui/mainwnd/ImageReferencesListWidget.h b/Grinder/ui/mainwnd/ImageReferencesListWidget.h index 00108585c447fc7784c95de3dbd33b1eeace77fb..aab2839b4d6e21717d037629aabfd6be67e1156c 100644 --- a/Grinder/ui/mainwnd/ImageReferencesListWidget.h +++ b/Grinder/ui/mainwnd/ImageReferencesListWidget.h @@ -24,10 +24,6 @@ namespace grndr public: ImageReferencesListWidget(QWidget* parent = nullptr); - public slots: - void nextImageReference() { advanceCurrentImageReference(); } - void previousImageReference() { advanceCurrentImageReference(true); } - protected: virtual std::unique_ptr<QMenu> createContextMenu() const override; @@ -43,6 +39,8 @@ namespace grndr private slots: void switchImageReference() { switchToObjectItem(currentObjectItem()); } + void nextImageReference() const; + void previousImageReference() const; void viewImageReference(); void addImageReferences(); void addImageReferences(QStringList files); @@ -53,9 +51,6 @@ namespace grndr void updateActions(); - private: - void advanceCurrentImageReference(bool goUp = false); - private: QAction* _switchImageReferenceAction{nullptr}; QAction* _nextImageReferenceAction{nullptr}; diff --git a/Grinder/ui/visscene/VisualNode.cpp b/Grinder/ui/visscene/VisualNode.cpp index dfcf513a91b60c2185e517dcfdd034493d9f8d9b..ea0ed9afbb17c7e47ef056e6378542cec987fc7e 100644 --- a/Grinder/ui/visscene/VisualNode.cpp +++ b/Grinder/ui/visscene/VisualNode.cpp @@ -5,6 +5,7 @@ #include "Grinder.h" #include "VisualNode.h" +#include "VisualSceneView.h" #include "util/UIUtils.h" #include <QMenu> @@ -84,5 +85,18 @@ void VisualNode::showContextMenu(QPoint pos) const } } - menu.exec(pos); + // Iterate over all scene views to add context menu entries from VisualSceneViews + for (const auto& view : _graphicsScene->views()) + { + if (auto visualView = dynamic_cast<VisualSceneView*>(view)) + { + if (_graphicsScene->selectedItems().size() > 1) + visualView->prepareNodesContextMenu(menu); + else + visualView->prepareNodeContextMenu(menu); + } + } + + if (!menu.isEmpty()) + menu.exec(pos); } diff --git a/Grinder/ui/visscene/VisualSceneView.cpp b/Grinder/ui/visscene/VisualSceneView.cpp index cb036364bbe7ce9b21d811b5447f8be8bcb34f79..6dec2a52b530698935c760a2f02f2731bed4862f 100644 --- a/Grinder/ui/visscene/VisualSceneView.cpp +++ b/Grinder/ui/visscene/VisualSceneView.cpp @@ -19,7 +19,7 @@ VisualSceneView::VisualSceneView(QWidget* parent) : MetaWidget(parent) // Create view actions _selectAllAction = UIUtils::createAction(this, "Select &all", FILE_ICON_SELECTALL, SLOT(selectAllItems()), "Select all items", "Ctrl+A"); - _deleteSelectedAction = UIUtils::createAction(this, "&Delete selected items", FILE_ICON_DELETE_SELECTED, SLOT(removeSelectedItems()), "Delete the selected items", "Del"); + _deleteSelectedAction = UIUtils::createAction(this, "&Delete selected item(s)", FILE_ICON_DELETE_SELECTED, SLOT(removeSelectedItems()), "Delete the selected items", "Del"); _zoomInAction = UIUtils::createAction(this, "Zoom &in", FILE_ICON_ZOOMIN, SLOT(zoomIn()), "Zoom the view in", "Ctrl++"); _zoomOutAction = UIUtils::createAction(this, "Zoom &out", FILE_ICON_ZOOMOUT, SLOT(zoomOut()), "Zoom the view out", "Ctrl+-"); diff --git a/Grinder/ui/visscene/VisualSceneView.h b/Grinder/ui/visscene/VisualSceneView.h index e540c18265d844d65c624681662ec1024f26dee7..2dc88892448d13dbd3538c9b32d46faa58879bf1 100644 --- a/Grinder/ui/visscene/VisualSceneView.h +++ b/Grinder/ui/visscene/VisualSceneView.h @@ -40,6 +40,9 @@ namespace grndr public: virtual const VisualSceneStyle& sceneStyle() const = 0; + virtual void prepareNodeContextMenu(QMenu& menu) const { Q_UNUSED(menu); } + virtual void prepareNodesContextMenu(QMenu& menu) const { Q_UNUSED(menu); } + signals: void zoomChanged(qreal); diff --git a/Grinder/util/SerializationUtils.h b/Grinder/util/SerializationUtils.h index 8190a56f897c44848178e151d96928bbf6db1aee..29dfe7d35c8bd21de940690c4ad5f1f38a5395f0 100644 --- a/Grinder/util/SerializationUtils.h +++ b/Grinder/util/SerializationUtils.h @@ -6,8 +6,8 @@ #ifndef SERIALIZATIONUTILS_H #define SERIALIZATIONUTILS_H -#include "project/serialization/SerializationContext.h" -#include "project/serialization/DeserializationContext.h" +#include "common/serialization/SerializationContext.h" +#include "common/serialization/DeserializationContext.h" namespace grndr { diff --git a/Grinder/util/UIUtils.cpp b/Grinder/util/UIUtils.cpp index d6cc7433ab9678b6ff4e70d82dfaf3a003bceb77..9e566d49f40e1cf15bb99186ff56c93f0535684a 100644 --- a/Grinder/util/UIUtils.cpp +++ b/Grinder/util/UIUtils.cpp @@ -24,11 +24,10 @@ QAction* UIUtils::createAction(QObject* owner, QString name, QString icon, const } if (!shortcut.isEmpty()) - { action->setShortcut(QKeySequence{shortcut}); - action->setShortcutContext(context); - action->setShortcutVisibleInContextMenu(true); - } + + action->setShortcutContext(context); + action->setShortcutVisibleInContextMenu(true); if (callback) owner->connect(action, SIGNAL(triggered(bool)), receiver ? receiver : owner, callback);