From 1839de431cfa2c75fcb4e4a26bf8abdbc5a8cbee Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 7 Dec 2022 17:03:28 +0200 Subject: [PATCH] QmlDesigner: Implement "Anchor to parent" for 2D-View Context Menu ParentAnchorAction is added to handle "Anchor to parent" actions. Task-number: QDS-8502 Change-Id: I629055fcf389c6c8dff3fc308180a9cdb57f26c9 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../components/componentcore/anchoraction.cpp | 133 ++++++++++++++++++ .../components/componentcore/anchoraction.h | 27 ++++ .../componentcore/componentcore_constants.h | 15 ++ .../componentcore/designeractionmanager.cpp | 67 +++++++++ 5 files changed, 243 insertions(+) create mode 100644 src/plugins/qmldesigner/components/componentcore/anchoraction.cpp create mode 100644 src/plugins/qmldesigner/components/componentcore/anchoraction.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 5daf2e72f80..9e040dba0e6 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -618,6 +618,7 @@ extend_qtc_plugin(QmlDesigner selectioncontext.cpp selectioncontext.h theme.cpp theme.h zoomaction.cpp zoomaction.h + anchoraction.cpp anchoraction.h svgpasteaction.cpp svgpasteaction.h viewmanager.cpp viewmanager.h ) diff --git a/src/plugins/qmldesigner/components/componentcore/anchoraction.cpp b/src/plugins/qmldesigner/components/componentcore/anchoraction.cpp new file mode 100644 index 00000000000..630a53dc39e --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/anchoraction.cpp @@ -0,0 +1,133 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "anchoraction.h" +#include "nodeabstractproperty.h" + +#include + +using namespace QmlDesigner; + +static void setAnchorToTheSameOnTarget(AbstractView *view, + const ModelNode &objectNode, + const AnchorLineType &objectAnchorType, + const ModelNode &targetNode, + double margin = 0) +{ + QmlItemNode node = objectNode; + QmlItemNode dstNode = targetNode; + if (!node.isValid() || !dstNode.isValid()) + return; + + view->executeInTransaction("QmlAnchorAction|setAnchorToTheSameOnTarget", [&] { + int maxFlagBits = 8 * sizeof(AnchorLineType); + for (int i = 0; i < maxFlagBits; ++i) { + AnchorLineType requiredAnchor = AnchorLineType(0x1 << i); + if (requiredAnchor & objectAnchorType) { + node.anchors().setAnchor(requiredAnchor, dstNode, requiredAnchor); + if (qFuzzyIsNull(margin)) + node.anchors().removeMargin(requiredAnchor); + else + node.anchors().setMargin(requiredAnchor, margin); + } + } + }); +} + +static void anchorToParent(const SelectionContext &selectionState, AnchorLineType anchorType) +{ + if (!selectionState.view()) + return; + + ModelNode modelNode = selectionState.currentSingleSelectedNode(); + ModelNode parentModelNode = modelNode.parentProperty().parentModelNode(); + setAnchorToTheSameOnTarget(selectionState.view(), modelNode, anchorType, parentModelNode); +} + +static void removeAnchor(const SelectionContext &selectionState, + const AnchorLineType &objectAnchorType, + double margin = 0) +{ + ModelNode srcModelNode = selectionState.currentSingleSelectedNode(); + QmlItemNode node = srcModelNode; + AbstractView *view = srcModelNode.view(); + if (!node.isValid() || !view) + return; + + view->executeInTransaction("QmlAnchorAction|removeAnchor", [&] { + + int maxFlagBits = 8 * sizeof(AnchorLineType); + for (int i = 0; i < maxFlagBits; ++i) { + AnchorLineType singleAnchor = AnchorLineType(0x1 << i); + if (singleAnchor & objectAnchorType) { + node.anchors().removeAnchor(singleAnchor); + if (qFuzzyIsNull(margin)) + node.anchors().removeMargin(singleAnchor); + else + node.anchors().setMargin(singleAnchor, margin); + } + } + }); +} + +static bool singleSelectionIsAnchoredToParentBy(const SelectionContext &selectionState, + const AnchorLineType &lineType) +{ + QmlItemNode itemNode(selectionState.currentSingleSelectedNode()); + QmlItemNode itemNodeParent(itemNode.modelParentItem()); + if (itemNode.isValid() && itemNodeParent.isValid()) { + bool allSet = false; + int maxFlagBits = 8 * sizeof(AnchorLineType); + for (int i = 0; i < maxFlagBits; ++i) { + AnchorLineType singleAnchor = AnchorLineType(0x1 << i); + if (singleAnchor & lineType) { + if (itemNode.anchors().modelAnchor(singleAnchor).qmlItemNode() == itemNodeParent) + allSet = true; + else + return false; + } + } + return allSet; + } + return false; +} + +static void toggleParentAnchor(const SelectionContext &selectionState, AnchorLineType anchorType) +{ + if (singleSelectionIsAnchoredToParentBy(selectionState, anchorType)) + removeAnchor(selectionState, anchorType); + else + anchorToParent(selectionState, anchorType); +} + +static SelectionContextOperation getSuitableAnchorFunction(AnchorLineType lineType) +{ + return std::bind(toggleParentAnchor, std::placeholders::_1, lineType); +} + +ParentAnchorAction::ParentAnchorAction(const QByteArray &id, + const QString &description, + const QIcon &icon, + const QString &tooltip, + const QByteArray &category, + const QKeySequence &key, + int priority, + AnchorLineType lineType) + : ModelNodeAction(id, + description, + icon, + tooltip, + category, + key, + priority, + getSuitableAnchorFunction(lineType), + &SelectionContextFunctors::singleSelectedItem) + , m_lineType(lineType) +{ + setCheckable(true); +} + +bool ParentAnchorAction::isChecked(const SelectionContext &selectionState) const +{ + return singleSelectionIsAnchoredToParentBy(selectionState, m_lineType); +} diff --git a/src/plugins/qmldesigner/components/componentcore/anchoraction.h b/src/plugins/qmldesigner/components/componentcore/anchoraction.h new file mode 100644 index 00000000000..29a190c0b73 --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/anchoraction.h @@ -0,0 +1,27 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +namespace QmlDesigner { + +class ParentAnchorAction : public ModelNodeAction +{ +public: + ParentAnchorAction(const QByteArray &id, + const QString &description, + const QIcon &icon, + const QString &tooltip, + const QByteArray &category, + const QKeySequence &key, + int priority, + AnchorLineType lineType); + + bool isChecked(const SelectionContext &selectionState) const override; + +private: + const AnchorLineType m_lineType; +}; + +} diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 6664eec8556..3a53555ccdd 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -41,6 +41,14 @@ const char applyFormatCommandId[] = "ApplyFormat"; const char visiblityCommandId[] = "ToggleVisiblity"; const char anchorsFillCommandId[] = "AnchorsFill"; const char anchorsResetCommandId[] = "AnchorsReset"; + +const char anchorParentTopAndBottomCommandId[] = "AnchorParentTopAndBottom"; +const char anchorParentLeftAndRightCommandId[] = "AnchorParentLeftAndRight"; +const char anchorParentTopCommandId[] = "AnchorParentTop"; +const char anchorParentRightCommandId[] = "AnchorParentRight"; +const char anchorParentBottomCommandId[] = "AnchorParentBottom"; +const char anchorParentLeftCommandId[] = "AnchorParentLeft"; + const char removePositionerCommandId[] = "RemovePositioner"; const char createFlowActionAreaCommandId[] = "CreateFlowActionArea"; const char setFlowStartCommandId[] = "SetFlowStart"; @@ -134,6 +142,13 @@ const char reverseDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "R const char anchorsFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fill Parent"); const char anchorsResetDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "No Anchors"); +const char anchorParentTopAndBottomDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Top And Bottom"); +const char anchorParentLeftAndRightDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Left And Right"); +const char anchorParentTopDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Top"); +const char anchorParentRightDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Right"); +const char anchorParentBottomDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Bottom"); +const char anchorParentLeftDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Left"); + const char layoutColumnPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Column Positioner"); const char layoutRowPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Row Positioner"); const char layoutGridPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Grid Positioner"); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 9df8ad0b511..dd07f433ca5 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -3,6 +3,7 @@ #include "designeractionmanager.h" +#include "anchoraction.h" #include "changestyleaction.h" #include "designeractionmanagerview.h" #include "designermcumanager.h" @@ -1526,6 +1527,72 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new SeperatorDesignerAction(anchorsCategory, 10)); + addDesignerAction(new ParentAnchorAction( + anchorParentTopAndBottomCommandId, + anchorParentTopAndBottomDisplayName, + {}, + {}, + anchorsCategory, + QKeySequence(), + 11, + AnchorLineType(AnchorLineTop | AnchorLineBottom))); + + addDesignerAction(new ParentAnchorAction( + anchorParentLeftAndRightCommandId, + anchorParentLeftAndRightDisplayName, + {}, + {}, + anchorsCategory, + QKeySequence(), + 12, + AnchorLineType(AnchorLineLeft | AnchorLineRight))); + + addDesignerAction(new SeperatorDesignerAction(anchorsCategory, 20)); + + addDesignerAction(new ParentAnchorAction( + anchorParentTopCommandId, + anchorParentTopDisplayName, + Utils::Icon({{":/qmldesigner/images/anchor_top.png", Utils::Theme::IconsBaseColor}, + {":/utils/images/iconoverlay_reset.png", Utils::Theme::IconsStopToolBarColor}}).icon(), + {}, + anchorsCategory, + QKeySequence(), + 21, + AnchorLineTop)); + + addDesignerAction(new ParentAnchorAction( + anchorParentBottomCommandId, + anchorParentBottomDisplayName, + Utils::Icon({{":/qmldesigner/images/anchor_bottom.png", Utils::Theme::IconsBaseColor}, + {":/utils/images/iconoverlay_reset.png", Utils::Theme::IconsStopToolBarColor}}).icon(), + {}, + anchorsCategory, + QKeySequence(), + 22, + AnchorLineBottom)); + + addDesignerAction(new ParentAnchorAction( + anchorParentLeftCommandId, + anchorParentLeftDisplayName, + Utils::Icon({{":/qmldesigner/images/anchor_left.png", Utils::Theme::IconsBaseColor}, + {":/utils/images/iconoverlay_reset.png", Utils::Theme::IconsStopToolBarColor}}).icon(), + {}, + anchorsCategory, + QKeySequence(), + 23, + AnchorLineLeft)); + + addDesignerAction(new ParentAnchorAction( + anchorParentRightCommandId, + anchorParentRightDisplayName, + Utils::Icon({{":/qmldesigner/images/anchor_right.png", Utils::Theme::IconsBaseColor}, + {":/utils/images/iconoverlay_reset.png", Utils::Theme::IconsStopToolBarColor}}).icon(), + {}, + anchorsCategory, + QKeySequence(), + 24, + AnchorLineRight)); + addDesignerAction(new ActionGroup( positionerCategoryDisplayName, positionerCategory,