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
|
selectioncontext.cpp selectioncontext.h
|
||||||
theme.cpp theme.h
|
theme.cpp theme.h
|
||||||
zoomaction.cpp zoomaction.h
|
zoomaction.cpp zoomaction.h
|
||||||
|
anchoraction.cpp anchoraction.h
|
||||||
svgpasteaction.cpp svgpasteaction.h
|
svgpasteaction.cpp svgpasteaction.h
|
||||||
viewmanager.cpp viewmanager.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 visiblityCommandId[] = "ToggleVisiblity";
|
||||||
const char anchorsFillCommandId[] = "AnchorsFill";
|
const char anchorsFillCommandId[] = "AnchorsFill";
|
||||||
const char anchorsResetCommandId[] = "AnchorsReset";
|
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 removePositionerCommandId[] = "RemovePositioner";
|
||||||
const char createFlowActionAreaCommandId[] = "CreateFlowActionArea";
|
const char createFlowActionAreaCommandId[] = "CreateFlowActionArea";
|
||||||
const char setFlowStartCommandId[] = "SetFlowStart";
|
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 anchorsFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fill Parent");
|
||||||
const char anchorsResetDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "No Anchors");
|
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 layoutColumnPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Column Positioner");
|
||||||
const char layoutRowPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Row Positioner");
|
const char layoutRowPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Row Positioner");
|
||||||
const char layoutGridPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Grid Positioner");
|
const char layoutGridPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Grid Positioner");
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "designeractionmanager.h"
|
#include "designeractionmanager.h"
|
||||||
|
|
||||||
|
#include "anchoraction.h"
|
||||||
#include "changestyleaction.h"
|
#include "changestyleaction.h"
|
||||||
#include "designeractionmanagerview.h"
|
#include "designeractionmanagerview.h"
|
||||||
#include "designermcumanager.h"
|
#include "designermcumanager.h"
|
||||||
@@ -1526,6 +1527,72 @@ void DesignerActionManager::createDefaultDesignerActions()
|
|||||||
|
|
||||||
addDesignerAction(new SeperatorDesignerAction(anchorsCategory, 10));
|
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(
|
addDesignerAction(new ActionGroup(
|
||||||
positionerCategoryDisplayName,
|
positionerCategoryDisplayName,
|
||||||
positionerCategory,
|
positionerCategory,
|
||||||
|
|||||||
Reference in New Issue
Block a user