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 <mahmoud.badri@qt.io>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Ali Kianian
2022-12-07 17:03:28 +02:00
parent fcf70d6957
commit 1839de431c
5 changed files with 243 additions and 0 deletions

View File

@@ -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
)

View File

@@ -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 <qmlanchors.h>
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);
}

View File

@@ -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 <modelnodecontextmenu_helper.h>
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;
};
}

View File

@@ -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");

View File

@@ -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,