forked from qt-creator/qt-creator
QmlDesigner: Support anchoring at snapping
Change-Id: I3ec504e931ee63761538acb4666a3c8ce1a592e5 Reviewed-by: Thomas Hartmann <Thomas.Hartmann@digia.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "abstractformeditortool.h"
|
||||
#include "formeditorview.h"
|
||||
#include "formeditorwidget.h"
|
||||
|
||||
#include <modelnodecontextmenu.h>
|
||||
|
||||
@@ -36,6 +37,7 @@
|
||||
#include <QGraphicsSceneDragDropEvent>
|
||||
#include <QMimeData>
|
||||
#include <nodemetainfo.h>
|
||||
#include <QAction>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
@@ -219,6 +221,22 @@ void AbstractFormEditorTool::showContextMenu(QGraphicsSceneMouseEvent *event)
|
||||
ModelNodeContextMenu::showContextMenu(view(), event->screenPos(), event->scenePos().toPoint(), true);
|
||||
}
|
||||
|
||||
Snapper::Snapping AbstractFormEditorTool::generateUseSnapping(Qt::KeyboardModifiers keyboardModifier) const
|
||||
{
|
||||
bool shouldSnapping = view()->formEditorWidget()->snappingAction()->isChecked();
|
||||
bool shouldSnappingAndAnchoring = view()->formEditorWidget()->snappingAndAnchoringAction()->isChecked();
|
||||
|
||||
Snapper::Snapping useSnapping = Snapper::NoSnapping;
|
||||
if (keyboardModifier.testFlag(Qt::ControlModifier) != (shouldSnapping || shouldSnappingAndAnchoring)) {
|
||||
if (shouldSnappingAndAnchoring)
|
||||
useSnapping = Snapper::UseSnappingAndAnchoring;
|
||||
else
|
||||
useSnapping = Snapper::UseSnapping;
|
||||
}
|
||||
|
||||
return useSnapping;
|
||||
}
|
||||
|
||||
void AbstractFormEditorTool::clear()
|
||||
{
|
||||
m_itemList.clear();
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
#include <qmldesignercorelib_global.h>
|
||||
|
||||
#include "snapper.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QGraphicsItem;
|
||||
QT_END_NAMESPACE
|
||||
@@ -99,6 +101,7 @@ public:
|
||||
protected:
|
||||
virtual void selectedItemsChanged(const QList<FormEditorItem*> &itemList) = 0;
|
||||
virtual void showContextMenu(QGraphicsSceneMouseEvent *event);
|
||||
Snapper::Snapping generateUseSnapping(Qt::KeyboardModifiers keyboardModifier) const;
|
||||
|
||||
FormEditorView *view() const;
|
||||
void setView(FormEditorView *view);
|
||||
|
||||
@@ -275,8 +275,8 @@ void DragTool::dropEvent(QGraphicsSceneDragDropEvent * event)
|
||||
if (event->mimeData()->hasFormat("application/vnd.bauhaus.itemlibraryinfo") ||
|
||||
event->mimeData()->hasFormat("application/vnd.bauhaus.libraryresource")) {
|
||||
event->accept();
|
||||
end(event->scenePos());
|
||||
//Q_ASSERT(m_token.isValid());
|
||||
end(generateUseSnapping(event->modifiers()));
|
||||
|
||||
try {
|
||||
m_rewriterTransaction.commit();
|
||||
} catch (RewritingException &e) {
|
||||
@@ -333,7 +333,9 @@ void DragTool::dragLeaveEvent(QGraphicsSceneDragDropEvent * event)
|
||||
event->accept();
|
||||
if (m_dragNode.isValid())
|
||||
m_dragNode.destroy();
|
||||
end(event->scenePos());
|
||||
|
||||
m_moveManipulator.end();
|
||||
clear();
|
||||
|
||||
try {
|
||||
m_rewriterTransaction.commit();
|
||||
@@ -367,7 +369,7 @@ void DragTool::dragMoveEvent(QGraphicsSceneDragDropEvent * event)
|
||||
|
||||
FormEditorItem *parentItem = calculateContainer(event->scenePos() + QPoint(2, 2));
|
||||
if (!parentItem) { //if there is no parent any more - the use left the scene
|
||||
end(event->scenePos());
|
||||
end();
|
||||
QmlDesignerItemLibraryDragAndDrop::CustomDragAndDrop::show();
|
||||
m_dragNode.destroy(); //delete the node then
|
||||
return;
|
||||
@@ -405,9 +407,15 @@ void DragTool::dragMoveEvent(QGraphicsSceneDragDropEvent * event)
|
||||
}
|
||||
}
|
||||
|
||||
void DragTool::end(QPointF scenePos)
|
||||
void DragTool::end()
|
||||
{
|
||||
m_moveManipulator.end(scenePos);
|
||||
m_moveManipulator.end();
|
||||
clear();
|
||||
}
|
||||
|
||||
void DragTool::end(Snapper::Snapping useSnapping)
|
||||
{
|
||||
m_moveManipulator.end(useSnapping);
|
||||
clear();
|
||||
}
|
||||
|
||||
@@ -424,7 +432,7 @@ void DragTool::move(QPointF scenePos)
|
||||
}
|
||||
|
||||
//MoveManipulator::Snapping useSnapping = MoveManipulator::NoSnapping;
|
||||
MoveManipulator::Snapping useSnapping = MoveManipulator::UseSnapping;
|
||||
Snapper::Snapping useSnapping = Snapper::UseSnapping;
|
||||
/* if (event->modifiers().testFlag(Qt::ControlModifier) != view()->isSnapButtonChecked())
|
||||
useSnapping = MoveManipulator::UseSnapping;*/
|
||||
|
||||
|
||||
@@ -116,7 +116,8 @@ private:
|
||||
QList<Import> missingImportList(const ItemLibraryEntry &itemLibraryEntry);
|
||||
|
||||
void begin(QPointF scenePos);
|
||||
void end(QPointF scenePos);
|
||||
void end();
|
||||
void end(Snapper::Snapping useSnapping);
|
||||
void move(QPointF scenePos);
|
||||
|
||||
MoveManipulator m_moveManipulator;
|
||||
|
||||
@@ -60,17 +60,23 @@ FormEditorWidget::FormEditorWidget(FormEditorView *view)
|
||||
|
||||
m_toolActionGroup = new QActionGroup(this);
|
||||
|
||||
m_transformToolAction = m_toolActionGroup->addAction(tr("Transform Tool (Q)."));
|
||||
m_transformToolAction->setShortcut(Qt::Key_Q);
|
||||
m_transformToolAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||
m_transformToolAction->setCheckable(true);
|
||||
m_transformToolAction->setChecked(true);
|
||||
m_transformToolAction->setIcon(QPixmap(":/icon/tool/transform.png"));
|
||||
connect(m_transformToolAction.data(), SIGNAL(triggered(bool)), SLOT(changeTransformTool(bool)));
|
||||
|
||||
|
||||
QActionGroup *layoutActionGroup = new QActionGroup(this);
|
||||
layoutActionGroup->setExclusive(false);
|
||||
layoutActionGroup->setExclusive(true);
|
||||
|
||||
m_noSnappingAction = layoutActionGroup->addAction(tr("No snapping (T)."));
|
||||
m_noSnappingAction->setShortcut(Qt::Key_W);
|
||||
m_noSnappingAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||
m_noSnappingAction->setCheckable(true);
|
||||
m_noSnappingAction->setChecked(true);
|
||||
m_noSnappingAction->setIcon(QPixmap(":/icon/layout/no_snapping.png"));
|
||||
|
||||
m_snappingAndAnchoringAction = layoutActionGroup->addAction(tr("Snapping with anchoring (W)."));
|
||||
m_snappingAndAnchoringAction->setShortcut(Qt::Key_W);
|
||||
m_snappingAndAnchoringAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||
m_snappingAndAnchoringAction->setCheckable(true);
|
||||
m_snappingAndAnchoringAction->setChecked(true);
|
||||
m_snappingAndAnchoringAction->setIcon(QPixmap(":/icon/layout/snapping_and_anchoring.png"));
|
||||
|
||||
m_snappingAction = layoutActionGroup->addAction(tr("Snap to guides (E)."));
|
||||
m_snappingAction->setShortcut(Qt::Key_E);
|
||||
m_snappingAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||
@@ -78,14 +84,6 @@ FormEditorWidget::FormEditorWidget(FormEditorView *view)
|
||||
m_snappingAction->setChecked(true);
|
||||
m_snappingAction->setIcon(QPixmap(":/icon/layout/snapping.png"));
|
||||
|
||||
m_snappingAndAnchoringAction = layoutActionGroup->addAction(tr("Toggle snapping and anchoring (R)."));
|
||||
m_snappingAndAnchoringAction->setShortcut(Qt::Key_R);
|
||||
m_snappingAndAnchoringAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||
m_snappingAndAnchoringAction->setCheckable(true);
|
||||
m_snappingAndAnchoringAction->setChecked(false);
|
||||
m_snappingAndAnchoringAction->setEnabled(false);
|
||||
m_snappingAndAnchoringAction->setVisible(false);
|
||||
m_snappingAndAnchoringAction->setIcon(QPixmap(":/icon/layout/snapping_and_anchoring.png"));
|
||||
|
||||
addActions(layoutActionGroup->actions());
|
||||
upperActions.append(layoutActionGroup->actions());
|
||||
@@ -125,8 +123,6 @@ FormEditorWidget::FormEditorWidget(FormEditorView *view)
|
||||
addAction(m_rootHeightAction.data());
|
||||
upperActions.append(m_rootHeightAction.data());
|
||||
|
||||
m_snappingAndAnchoringAction = layoutActionGroup->addAction(tr("Toggle snapping and anchoring (R)."));
|
||||
|
||||
m_toolBox = new ToolBox(this);
|
||||
fillLayout->addWidget(m_toolBox.data());
|
||||
m_toolBox->setLeftSideActions(upperActions);
|
||||
@@ -246,11 +242,6 @@ ZoomAction *FormEditorWidget::zoomAction() const
|
||||
return m_zoomAction.data();
|
||||
}
|
||||
|
||||
QAction *FormEditorWidget::transformToolAction() const
|
||||
{
|
||||
return m_transformToolAction.data();
|
||||
}
|
||||
|
||||
QAction *FormEditorWidget::showBoundingRectAction() const
|
||||
{
|
||||
return m_showBoundingRectAction.data();
|
||||
|
||||
@@ -56,7 +56,6 @@ public:
|
||||
FormEditorWidget(FormEditorView *view);
|
||||
|
||||
ZoomAction *zoomAction() const;
|
||||
QAction *transformToolAction() const;
|
||||
QAction *showBoundingRectAction() const;
|
||||
QAction *selectOnlyContentItemsAction() const;
|
||||
QAction *snappingAction() const;
|
||||
|
||||
@@ -102,7 +102,7 @@ void MoveManipulator::synchronizeParent(const QList<FormEditorItem*> &itemList,
|
||||
}
|
||||
|
||||
if (!parentNode.metaInfo().isSubclassOf("<cpp>.QDeclarativeBasePositioner", -1, -1))
|
||||
update(m_lastPosition, NoSnapping, UseBaseState);
|
||||
update(m_lastPosition, Snapper::NoSnapping, UseBaseState);
|
||||
}
|
||||
|
||||
void MoveManipulator::synchronizeInstanceParent(const QList<FormEditorItem*> &itemList)
|
||||
@@ -260,7 +260,7 @@ QHash<FormEditorItem*, QRectF> MoveManipulator::tanslatedBoundingRects(const QHa
|
||||
/*
|
||||
/brief updates the position of the items.
|
||||
*/
|
||||
void MoveManipulator::update(const QPointF& updatePoint, Snapping useSnapping, State stateToBeManipulated)
|
||||
void MoveManipulator::update(const QPointF& updatePoint, Snapper::Snapping useSnapping, State stateToBeManipulated)
|
||||
{
|
||||
m_lastPosition = updatePoint;
|
||||
deleteSnapLines(); //Since they position is changed and the item is moved the snapping lines are
|
||||
@@ -275,12 +275,7 @@ void MoveManipulator::update(const QPointF& updatePoint, Snapping useSnapping, S
|
||||
|
||||
QPointF offsetVector(updatePointInContainerSpace - beginPointInContainerSpace);
|
||||
|
||||
if (useSnapping == UseSnappingAndAnchoring)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if (useSnapping == UseSnapping || useSnapping == UseSnappingAndAnchoring) {
|
||||
if (useSnapping == Snapper::UseSnapping || useSnapping == Snapper::UseSnappingAndAnchoring) {
|
||||
offsetVector -= findSnappingOffset(tanslatedBoundingRects(m_beginItemRectHash, offsetVector));
|
||||
generateSnappingLines(tanslatedBoundingRects(m_beginItemRectHash, offsetVector));
|
||||
}
|
||||
@@ -386,15 +381,25 @@ void MoveManipulator::reparentTo(FormEditorItem *newParent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MoveManipulator::end(const QPointF &/*endPoint*/)
|
||||
void MoveManipulator::end()
|
||||
{
|
||||
m_isActive = false;
|
||||
deleteSnapLines();
|
||||
// setOpacityForAllElements(1.0);
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MoveManipulator::end(Snapper::Snapping useSnapping)
|
||||
{
|
||||
if (useSnapping == Snapper::UseSnappingAndAnchoring) {
|
||||
foreach (FormEditorItem *formEditorItem, m_itemList)
|
||||
m_snapper.adjustAnchoringOfItem(formEditorItem);
|
||||
}
|
||||
|
||||
end();
|
||||
}
|
||||
|
||||
void MoveManipulator::moveBy(double deltaX, double deltaY)
|
||||
{
|
||||
foreach (FormEditorItem* item, m_itemList) {
|
||||
|
||||
@@ -49,12 +49,6 @@ class Model;
|
||||
class MoveManipulator
|
||||
{
|
||||
public:
|
||||
enum Snapping {
|
||||
UseSnapping,
|
||||
UseSnappingAndAnchoring,
|
||||
NoSnapping
|
||||
};
|
||||
|
||||
enum State {
|
||||
UseActualState,
|
||||
UseBaseState
|
||||
@@ -68,9 +62,10 @@ public:
|
||||
void synchronizeParent(const QList<FormEditorItem*> &itemList, const ModelNode &parentNode);
|
||||
|
||||
void begin(const QPointF& beginPoint);
|
||||
void update(const QPointF& updatePoint, Snapping useSnapping, State stateToBeManipulated = UseActualState);
|
||||
void update(const QPointF& updatePoint, Snapper::Snapping useSnapping, State stateToBeManipulated = UseActualState);
|
||||
void reparentTo(FormEditorItem *newParent);
|
||||
void end(const QPointF& endPoint);
|
||||
void end();
|
||||
void end(Snapper::Snapping useSnapping);
|
||||
|
||||
void moveBy(double deltaX, double deltaY);
|
||||
|
||||
@@ -101,6 +96,8 @@ protected:
|
||||
|
||||
void setPosition(QmlItemNode itemNode, const QPointF &position);
|
||||
|
||||
void adjustAnchoringOfItem(FormEditorItem *item);
|
||||
|
||||
private:
|
||||
Snapper m_snapper;
|
||||
QWeakPointer<LayerItem> m_layerItem;
|
||||
|
||||
@@ -103,18 +103,7 @@ void MoveTool::mouseMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldSnapping = view()->formEditorWidget()->snappingAction()->isChecked();
|
||||
bool shouldSnappingAndAnchoring = view()->formEditorWidget()->snappingAndAnchoringAction()->isChecked();
|
||||
|
||||
MoveManipulator::Snapping useSnapping = MoveManipulator::NoSnapping;
|
||||
if (event->modifiers().testFlag(Qt::ControlModifier) != (shouldSnapping || shouldSnappingAndAnchoring)) {
|
||||
if (shouldSnappingAndAnchoring)
|
||||
useSnapping = MoveManipulator::UseSnappingAndAnchoring;
|
||||
else
|
||||
useSnapping = MoveManipulator::UseSnapping;
|
||||
}
|
||||
|
||||
m_moveManipulator.update(event->scenePos(), useSnapping);
|
||||
m_moveManipulator.update(event->scenePos(), generateUseSnapping(event->modifiers()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,25 +203,11 @@ void MoveTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
|
||||
if (m_movingItems.isEmpty())
|
||||
return;
|
||||
|
||||
QLineF moveVector(event->scenePos(), m_moveManipulator.beginPoint());
|
||||
if (moveVector.length() < QApplication::startDragDistance())
|
||||
{
|
||||
QPointF beginPoint(m_moveManipulator.beginPoint());
|
||||
m_moveManipulator.end(generateUseSnapping(event->modifiers()));
|
||||
|
||||
m_moveManipulator.end(beginPoint);
|
||||
|
||||
// m_selectionIndicator.show();
|
||||
m_resizeIndicator.show();
|
||||
m_movingItems.clear();
|
||||
|
||||
view()->changeToSelectionTool(event);
|
||||
} else {
|
||||
m_moveManipulator.end(event->scenePos());
|
||||
|
||||
m_selectionIndicator.show();
|
||||
m_resizeIndicator.show();
|
||||
m_movingItems.clear();
|
||||
}
|
||||
m_selectionIndicator.show();
|
||||
m_resizeIndicator.show();
|
||||
m_movingItems.clear();
|
||||
}
|
||||
|
||||
AbstractFormEditorTool::mouseReleaseEvent(itemList, event);
|
||||
|
||||
@@ -99,14 +99,14 @@ void ResizeManipulator::begin(const QPointF &/*beginPoint*/)
|
||||
// return QSizeF(sizeAsPoint.x(), sizeAsPoint.y());
|
||||
//}
|
||||
|
||||
void ResizeManipulator::update(const QPointF& updatePoint, Snapping useSnapping)
|
||||
void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping useSnapping)
|
||||
{
|
||||
const double minimumWidth = 0.0;
|
||||
const double minimumHeight = 0.0;
|
||||
|
||||
deleteSnapLines();
|
||||
|
||||
bool snap = useSnapping == UseSnapping || useSnapping == UseSnappingAndAnchoring;
|
||||
bool snap = useSnapping == Snapper::UseSnapping || useSnapping == Snapper::UseSnappingAndAnchoring;
|
||||
|
||||
if (m_resizeController.isValid()) {
|
||||
|
||||
@@ -376,8 +376,15 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapping useSnapping)
|
||||
}
|
||||
}
|
||||
|
||||
void ResizeManipulator::end()
|
||||
void ResizeManipulator::end(Snapper::Snapping useSnapping)
|
||||
{
|
||||
if (useSnapping == Snapper::UseSnappingAndAnchoring) {
|
||||
deleteSnapLines();
|
||||
m_snapper.setTransformtionSpaceFormEditorItem(m_snapper.containerFormEditorItem());
|
||||
m_snapper.updateSnappingLines(m_resizeController.formEditorItem());
|
||||
m_snapper.adjustAnchoringOfItem(m_resizeController.formEditorItem());
|
||||
}
|
||||
|
||||
m_isActive = false;
|
||||
m_rewriterTransaction.commit();
|
||||
clear();
|
||||
|
||||
@@ -44,12 +44,6 @@ class Model;
|
||||
class ResizeManipulator
|
||||
{
|
||||
public:
|
||||
enum Snapping {
|
||||
UseSnapping,
|
||||
UseSnappingAndAnchoring,
|
||||
NoSnapping
|
||||
};
|
||||
|
||||
ResizeManipulator(LayerItem *layerItem, FormEditorView *view);
|
||||
~ResizeManipulator();
|
||||
|
||||
@@ -57,8 +51,8 @@ public:
|
||||
void removeHandle();
|
||||
|
||||
void begin(const QPointF& beginPoint);
|
||||
void update(const QPointF& updatePoint, Snapping useSnapping);
|
||||
void end();
|
||||
void update(const QPointF& updatePoint, Snapper::Snapping useSnapping);
|
||||
void end(Snapper::Snapping useSnapping);
|
||||
|
||||
void moveBy(double deltaX, double deltaY);
|
||||
|
||||
|
||||
@@ -75,20 +75,8 @@ void ResizeTool::mousePressEvent(const QList<QGraphicsItem*> &itemList,
|
||||
void ResizeTool::mouseMoveEvent(const QList<QGraphicsItem*> &,
|
||||
QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (m_resizeManipulator.isActive()) {
|
||||
bool shouldSnapping = view()->formEditorWidget()->snappingAction()->isChecked();
|
||||
bool shouldSnappingAndAnchoring = view()->formEditorWidget()->snappingAndAnchoringAction()->isChecked();
|
||||
|
||||
ResizeManipulator::Snapping useSnapping = ResizeManipulator::NoSnapping;
|
||||
if (event->modifiers().testFlag(Qt::ControlModifier) != (shouldSnapping || shouldSnappingAndAnchoring)) {
|
||||
if (shouldSnappingAndAnchoring)
|
||||
useSnapping = ResizeManipulator::UseSnappingAndAnchoring;
|
||||
else
|
||||
useSnapping = ResizeManipulator::UseSnapping;
|
||||
}
|
||||
|
||||
m_resizeManipulator.update(event->scenePos(), useSnapping);
|
||||
}
|
||||
if (m_resizeManipulator.isActive())
|
||||
m_resizeManipulator.update(event->scenePos(), generateUseSnapping(event->modifiers()));
|
||||
}
|
||||
|
||||
void ResizeTool::hoverMoveEvent(const QList<QGraphicsItem*> &itemList,
|
||||
@@ -128,7 +116,7 @@ void ResizeTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList,
|
||||
|
||||
m_selectionIndicator.show();
|
||||
m_resizeIndicator.show();
|
||||
m_resizeManipulator.end();
|
||||
m_resizeManipulator.end(generateUseSnapping(event->modifiers()));
|
||||
}
|
||||
|
||||
AbstractFormEditorTool::mouseReleaseEvent(itemList, event);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <QLineF>
|
||||
#include <QPen>
|
||||
#include <QApplication>
|
||||
#include <qmlanchors.h>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
@@ -525,11 +526,6 @@ static QList<QLineF> mergedHorizontalLines(const QList<QLineF> &lineList)
|
||||
return mergedLineList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static QList<QLineF> mergedVerticalLines(const QList<QLineF> &lineList)
|
||||
{
|
||||
QList<QLineF> mergedLineList;
|
||||
@@ -567,6 +563,155 @@ QList<QGraphicsItem*> Snapper::generateSnappingLines(const QRectF &boundingRect,
|
||||
return generateSnappingLines(boundingRectList, layerItem, transform);
|
||||
}
|
||||
|
||||
static QmlItemNode findItemOnSnappingLine(const QmlItemNode &sourceQmlItemNode, const SnapLineMap &snappingLines, double anchorLine, AnchorLine::Type anchorLineType)
|
||||
{
|
||||
QmlItemNode targetQmlItemNode;
|
||||
double targetAnchorLine = 0.0;
|
||||
|
||||
targetAnchorLine = std::numeric_limits<double>::max();
|
||||
|
||||
AnchorLine::Type compareAnchorLineType;
|
||||
|
||||
if (anchorLineType == AnchorLine::Left
|
||||
|| anchorLineType == AnchorLine::Right)
|
||||
compareAnchorLineType = AnchorLine::Top;
|
||||
else
|
||||
compareAnchorLineType = AnchorLine::Left;
|
||||
|
||||
SnapLineMapIterator snapLineIterator(snappingLines);
|
||||
while (snapLineIterator.hasNext()) {
|
||||
snapLineIterator.next();
|
||||
double snapLine = snapLineIterator.key();
|
||||
|
||||
if (qAbs(snapLine - anchorLine ) < 1.0) {
|
||||
QmlItemNode possibleAchorItemNode = snapLineIterator.value().second->qmlItemNode();
|
||||
|
||||
double currentToAnchorLine = possibleAchorItemNode.anchors().instanceAnchorLine(compareAnchorLineType);
|
||||
|
||||
if (possibleAchorItemNode != sourceQmlItemNode) {
|
||||
if (sourceQmlItemNode.instanceParent() == possibleAchorItemNode) {
|
||||
targetQmlItemNode = possibleAchorItemNode;
|
||||
targetAnchorLine = currentToAnchorLine;
|
||||
break;
|
||||
} else if (currentToAnchorLine < targetAnchorLine) {
|
||||
targetQmlItemNode = possibleAchorItemNode;
|
||||
targetAnchorLine = currentToAnchorLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return targetQmlItemNode;
|
||||
}
|
||||
|
||||
static void adjustAnchorLine(const QmlItemNode &sourceQmlItemNode,
|
||||
const QmlItemNode &containerQmlItemNode,
|
||||
const SnapLineMap &snappingLines,
|
||||
const SnapLineMap &snappingOffsets,
|
||||
AnchorLine::Type lineAnchorLineType,
|
||||
AnchorLine::Type offsetAnchorLineType)
|
||||
{
|
||||
QmlAnchors qmlAnchors = sourceQmlItemNode.anchors();
|
||||
|
||||
double fromAnchorLine = sourceQmlItemNode.anchors().instanceAnchorLine(lineAnchorLineType);
|
||||
QmlItemNode targetQmlItemNode = findItemOnSnappingLine(sourceQmlItemNode, snappingLines, fromAnchorLine, lineAnchorLineType);
|
||||
|
||||
if (targetQmlItemNode.isValid() && !targetQmlItemNode.anchors().checkForCycle(lineAnchorLineType, sourceQmlItemNode)) {
|
||||
double margin = 0.0;
|
||||
|
||||
if (targetQmlItemNode == containerQmlItemNode) {
|
||||
if (lineAnchorLineType == AnchorLine::Left
|
||||
|| lineAnchorLineType == AnchorLine::Top)
|
||||
margin = fromAnchorLine;
|
||||
else if (lineAnchorLineType == AnchorLine::Right)
|
||||
margin = targetQmlItemNode.instanceSize().width() - fromAnchorLine;
|
||||
else if (lineAnchorLineType == AnchorLine::Bottom)
|
||||
margin = targetQmlItemNode.instanceSize().height() - fromAnchorLine;
|
||||
|
||||
}
|
||||
|
||||
if (!qFuzzyIsNull(margin) || !qFuzzyIsNull(qmlAnchors.instanceMargin(lineAnchorLineType)))
|
||||
qmlAnchors.setMargin(lineAnchorLineType, margin);
|
||||
|
||||
qmlAnchors.setAnchor(lineAnchorLineType, targetQmlItemNode, lineAnchorLineType);
|
||||
} else if (!snappingOffsets.isEmpty()) {
|
||||
targetQmlItemNode = findItemOnSnappingLine(sourceQmlItemNode, snappingOffsets, fromAnchorLine, lineAnchorLineType);
|
||||
if (targetQmlItemNode.isValid() && !targetQmlItemNode.anchors().checkForCycle(lineAnchorLineType, sourceQmlItemNode)) {
|
||||
double margin = fromAnchorLine - targetQmlItemNode.anchors().instanceAnchorLine(offsetAnchorLineType);
|
||||
|
||||
if (lineAnchorLineType == AnchorLine::Right
|
||||
|| lineAnchorLineType == AnchorLine::Bottom)
|
||||
margin *= -1.;
|
||||
|
||||
|
||||
if (!qFuzzyIsNull(margin) || !qFuzzyIsNull(qmlAnchors.instanceMargin(lineAnchorLineType)))
|
||||
qmlAnchors.setMargin(lineAnchorLineType, margin);
|
||||
|
||||
qmlAnchors.setAnchor(lineAnchorLineType, targetQmlItemNode, offsetAnchorLineType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Snapper::adjustAnchoringOfItem(FormEditorItem *formEditorItem)
|
||||
{
|
||||
QmlItemNode qmlItemNode = formEditorItem->qmlItemNode();
|
||||
QmlAnchors qmlAnchors = qmlItemNode.anchors();
|
||||
|
||||
if (!qmlAnchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) {
|
||||
adjustAnchorLine(qmlItemNode,
|
||||
containerFormEditorItem()->qmlItemNode(),
|
||||
containerFormEditorItem()->leftSnappingLines(),
|
||||
containerFormEditorItem()->rightSnappingOffsets(),
|
||||
AnchorLine::Left,
|
||||
AnchorLine::Right);
|
||||
}
|
||||
|
||||
if (!qmlAnchors.instanceHasAnchor(AnchorLine::VerticalCenter)) {
|
||||
adjustAnchorLine(qmlItemNode,
|
||||
containerFormEditorItem()->qmlItemNode(),
|
||||
containerFormEditorItem()->topSnappingLines(),
|
||||
containerFormEditorItem()->bottomSnappingOffsets(),
|
||||
AnchorLine::Top,
|
||||
AnchorLine::Bottom);
|
||||
}
|
||||
|
||||
if (!qmlAnchors.instanceHasAnchor(AnchorLine::VerticalCenter)) {
|
||||
adjustAnchorLine(qmlItemNode,
|
||||
containerFormEditorItem()->qmlItemNode(),
|
||||
containerFormEditorItem()->bottomSnappingLines(),
|
||||
containerFormEditorItem()->topSnappingOffsets(),
|
||||
AnchorLine::Bottom,
|
||||
AnchorLine::Top);
|
||||
}
|
||||
|
||||
if (!qmlAnchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) {
|
||||
adjustAnchorLine(qmlItemNode,
|
||||
containerFormEditorItem()->qmlItemNode(),
|
||||
containerFormEditorItem()->rightSnappingLines(),
|
||||
containerFormEditorItem()->leftSnappingOffsets(),
|
||||
AnchorLine::Right,
|
||||
AnchorLine::Left);
|
||||
}
|
||||
|
||||
if (!qmlAnchors.instanceHasAnchor(AnchorLine::Left) && !qmlAnchors.instanceHasAnchor(AnchorLine::Right)) {
|
||||
adjustAnchorLine(qmlItemNode,
|
||||
containerFormEditorItem()->qmlItemNode(),
|
||||
containerFormEditorItem()->verticalCenterSnappingLines(),
|
||||
SnapLineMap(),
|
||||
AnchorLine::HorizontalCenter,
|
||||
AnchorLine::HorizontalCenter);
|
||||
}
|
||||
|
||||
if (!qmlAnchors.instanceHasAnchor(AnchorLine::Top) && !qmlAnchors.instanceHasAnchor(AnchorLine::Bottom)) {
|
||||
adjustAnchorLine(qmlItemNode,
|
||||
containerFormEditorItem()->qmlItemNode(),
|
||||
containerFormEditorItem()->horizontalCenterSnappingLines(),
|
||||
SnapLineMap(),
|
||||
AnchorLine::VerticalCenter,
|
||||
AnchorLine::VerticalCenter);
|
||||
}
|
||||
}
|
||||
|
||||
//static void alignLine(QLineF &line)
|
||||
//{
|
||||
// line.setP1(QPointF(std::floor(line.p1().x()) + 0.5,
|
||||
|
||||
@@ -42,6 +42,12 @@ namespace QmlDesigner {
|
||||
class Snapper
|
||||
{
|
||||
public:
|
||||
enum Snapping {
|
||||
UseSnapping,
|
||||
UseSnappingAndAnchoring,
|
||||
NoSnapping
|
||||
};
|
||||
|
||||
Snapper();
|
||||
|
||||
void setContainerFormEditorItem(FormEditorItem *formEditorItem);
|
||||
@@ -73,6 +79,9 @@ public:
|
||||
QList<QGraphicsItem*> generateSnappingLines(const QRectF &boundingRect,
|
||||
QGraphicsItem *layerItem,
|
||||
const QTransform &transform);
|
||||
|
||||
void adjustAnchoringOfItem(FormEditorItem *formEditorItem);
|
||||
|
||||
protected:
|
||||
double snappedOffsetForLines(const SnapLineMap &snappingLineMap,
|
||||
double value) const;
|
||||
|
||||
@@ -92,6 +92,14 @@ public:
|
||||
void removeAnchors();
|
||||
bool instanceHasAnchor(AnchorLine::Type sourceAnchorLineType) const;
|
||||
bool instanceHasAnchors() const;
|
||||
double instanceLeftAnchorLine() const;
|
||||
double instanceTopAnchorLine() const;
|
||||
double instanceRightAnchorLine() const;
|
||||
double instanceBottomAnchorLine() const;
|
||||
double instanceHorizontalCenterAnchorLine() const;
|
||||
double instanceVerticalCenterAnchorLine() const;
|
||||
double instanceAnchorLine(AnchorLine::Type anchorLine) const;
|
||||
|
||||
void setMargin(AnchorLine::Type sourceAnchorLineType, double margin) const;
|
||||
bool instanceHasMargin(AnchorLine::Type sourceAnchorLineType) const;
|
||||
double instanceMargin(AnchorLine::Type sourceAnchorLineType) const;
|
||||
@@ -101,8 +109,13 @@ public:
|
||||
void fill();
|
||||
void centerIn();
|
||||
|
||||
protected:
|
||||
bool checkForCycle(AnchorLine::Type anchorLineTyp, const QmlItemNode &sourceItem) const;
|
||||
bool checkForHorizontalCycle(const QmlItemNode &sourceItem) const;
|
||||
bool checkForVerticalCycle(const QmlItemNode &sourceItem) const;
|
||||
|
||||
QmlItemNode qmlItemNode() const;
|
||||
|
||||
protected:
|
||||
void beautify();
|
||||
|
||||
private:
|
||||
|
||||
@@ -393,7 +393,52 @@ bool QmlAnchors::instanceHasAnchors() const
|
||||
instanceHasAnchor(AnchorLine::Bottom) ||
|
||||
instanceHasAnchor(AnchorLine::HorizontalCenter) ||
|
||||
instanceHasAnchor(AnchorLine::VerticalCenter) ||
|
||||
instanceHasAnchor(AnchorLine::Baseline);
|
||||
instanceHasAnchor(AnchorLine::Baseline);
|
||||
}
|
||||
|
||||
double QmlAnchors::instanceLeftAnchorLine() const
|
||||
{
|
||||
return qmlItemNode().nodeInstance().position().x();
|
||||
}
|
||||
|
||||
double QmlAnchors::instanceTopAnchorLine() const
|
||||
{
|
||||
return qmlItemNode().nodeInstance().position().y();
|
||||
}
|
||||
|
||||
double QmlAnchors::instanceRightAnchorLine() const
|
||||
{
|
||||
return qmlItemNode().nodeInstance().position().x() + qmlItemNode().nodeInstance().size().width();
|
||||
}
|
||||
|
||||
double QmlAnchors::instanceBottomAnchorLine() const
|
||||
{
|
||||
return qmlItemNode().nodeInstance().position().y() + qmlItemNode().nodeInstance().size().height();
|
||||
}
|
||||
|
||||
double QmlAnchors::instanceHorizontalCenterAnchorLine() const
|
||||
{
|
||||
return (instanceLeftAnchorLine() + instanceRightAnchorLine()) / 2.0;
|
||||
}
|
||||
|
||||
double QmlAnchors::instanceVerticalCenterAnchorLine() const
|
||||
{
|
||||
return (instanceBottomAnchorLine() + instanceTopAnchorLine()) / 2.0;
|
||||
}
|
||||
|
||||
double QmlAnchors::instanceAnchorLine(AnchorLine::Type anchorLine) const
|
||||
{
|
||||
switch (anchorLine) {
|
||||
case AnchorLine::Left: return instanceLeftAnchorLine();
|
||||
case AnchorLine::Top: return instanceTopAnchorLine();
|
||||
case AnchorLine::Bottom: return instanceBottomAnchorLine();
|
||||
case AnchorLine::Right: return instanceRightAnchorLine();
|
||||
case AnchorLine::HorizontalCenter: return instanceHorizontalCenterAnchorLine();
|
||||
case AnchorLine::VerticalCenter: return instanceVerticalCenterAnchorLine();
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void QmlAnchors::setMargin(AnchorLine::Type sourceAnchorLineType, double margin) const
|
||||
@@ -407,6 +452,71 @@ bool QmlAnchors::instanceHasMargin(AnchorLine::Type sourceAnchorLineType) const
|
||||
return !qIsNull(instanceMargin(sourceAnchorLineType));
|
||||
}
|
||||
|
||||
static bool checkForHorizontalCycleRecusive(const QmlAnchors &anchors, QList<QmlItemNode> &visitedItems)
|
||||
{
|
||||
visitedItems.append(anchors.qmlItemNode());
|
||||
if (anchors.instanceHasAnchor(AnchorLine::Left)) {
|
||||
AnchorLine leftAnchorLine = anchors.instanceAnchor(AnchorLine::Left);
|
||||
if (visitedItems.contains(leftAnchorLine.qmlItemNode()) || checkForHorizontalCycleRecusive(leftAnchorLine.qmlItemNode().anchors(), visitedItems))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (anchors.instanceHasAnchor(AnchorLine::Right)) {
|
||||
AnchorLine rightAnchorLine = anchors.instanceAnchor(AnchorLine::Right);
|
||||
if (visitedItems.contains(rightAnchorLine.qmlItemNode()) || checkForHorizontalCycleRecusive(rightAnchorLine.qmlItemNode().anchors(), visitedItems))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) {
|
||||
AnchorLine horizontalCenterAnchorLine = anchors.instanceAnchor(AnchorLine::HorizontalCenter);
|
||||
if (visitedItems.contains(horizontalCenterAnchorLine.qmlItemNode()) || checkForHorizontalCycleRecusive(horizontalCenterAnchorLine.qmlItemNode().anchors(), visitedItems))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool checkForVerticalCycleRecusive(const QmlAnchors &anchors, QList<QmlItemNode> &visitedItems)
|
||||
{
|
||||
visitedItems.append(anchors.qmlItemNode());
|
||||
|
||||
if (anchors.instanceHasAnchor(AnchorLine::Top)) {
|
||||
AnchorLine topAnchorLine = anchors.instanceAnchor(AnchorLine::Top);
|
||||
if (visitedItems.contains(topAnchorLine.qmlItemNode()) || checkForVerticalCycleRecusive(topAnchorLine.qmlItemNode().anchors(), visitedItems))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (anchors.instanceHasAnchor(AnchorLine::Bottom)) {
|
||||
AnchorLine bottomAnchorLine = anchors.instanceAnchor(AnchorLine::Bottom);
|
||||
if (visitedItems.contains(bottomAnchorLine.qmlItemNode()) || checkForVerticalCycleRecusive(bottomAnchorLine.qmlItemNode().anchors(), visitedItems))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) {
|
||||
AnchorLine verticalCenterAnchorLine = anchors.instanceAnchor(AnchorLine::VerticalCenter);
|
||||
if (visitedItems.contains(verticalCenterAnchorLine.qmlItemNode()) || checkForVerticalCycleRecusive(verticalCenterAnchorLine.qmlItemNode().anchors(), visitedItems))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QmlAnchors::checkForHorizontalCycle(const QmlItemNode &sourceItem) const
|
||||
{
|
||||
QList<QmlItemNode> visitedItems;
|
||||
visitedItems.append(sourceItem);
|
||||
|
||||
return checkForHorizontalCycleRecusive(*this, visitedItems);
|
||||
}
|
||||
|
||||
bool QmlAnchors::checkForVerticalCycle(const QmlItemNode &sourceItem) const
|
||||
{
|
||||
QList<QmlItemNode> visitedItems;
|
||||
visitedItems.append(sourceItem);
|
||||
|
||||
return checkForVerticalCycleRecusive(*this, visitedItems);
|
||||
}
|
||||
|
||||
double QmlAnchors::instanceMargin(AnchorLine::Type sourceAnchorLineType) const
|
||||
{
|
||||
return qmlItemNode().nodeInstance().property(marginPropertyName(sourceAnchorLineType)).toDouble();
|
||||
@@ -452,4 +562,12 @@ void QmlAnchors::centerIn()
|
||||
qmlItemNode().modelNode().bindingProperty("anchors.centerIn").setExpression("parent");
|
||||
}
|
||||
|
||||
bool QmlAnchors::checkForCycle(AnchorLine::Type anchorLineTyp, const QmlItemNode &sourceItem) const
|
||||
{
|
||||
if (anchorLineTyp & AnchorLine::HorizontalMask)
|
||||
return checkForHorizontalCycle(sourceItem);
|
||||
else
|
||||
return checkForVerticalCycle(sourceItem);
|
||||
}
|
||||
|
||||
} //QmlDesigner
|
||||
|
||||
Reference in New Issue
Block a user