forked from qt-creator/qt-creator
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:
@@ -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
|
||||
)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user