123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "EditorCommon.h"
- #include "ViewportNudge.h"
- #include "ViewportElement.h"
- #include "QtHelpers.h"
- #include "GuideHelpers.h"
- #include "AlignToolbarSection.h"
- #include "ViewportMoveInteraction.h"
- #include "ViewportMoveGuideInteraction.h"
- #include <LyShine/UiComponentTypes.h>
- #include <LyShine/Bus/UiEditorCanvasBus.h>
- #include <AzCore/Casting/numeric_cast.h>
- #include <AzCore/Math/Vector2.h>
- #include <AzQtComponents/Components/Widgets/ToolBar.h>
- #include <QKeyEvent>
- #include <QMouseEvent>
- #include <QToolButton>
- #include <QTextDocumentFragment>
- #include <QSettings>
- #include <Editor/Resource.h>
- #include <Editor/Util/EditorUtils.h>
- static const float g_elementEdgeForgiveness = 10.0f;
- // The square of the minimum corner-to-corner distance for an area selection
- static const float g_minAreaSelectionDistance2 = 100.0f;
- #define UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_INTERACTION_MODE_KEY "ViewportWidget::m_interactionMode"
- #define UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_INTERACTION_MODE_DEFAULT ( ViewportInteraction::InteractionMode::SELECTION )
- #define UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_COORDINATE_SYSTEM_KEY "ViewportWidget::m_coordinateSystem"
- #define UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_COORDINATE_SYSTEM_DEFAULT ( ViewportInteraction::CoordinateSystem::LOCAL )
- namespace
- {
- const float defaultCanvasToViewportScaleIncrement = 0.20f;
- ViewportInteraction::InteractionMode PersistentGetInteractionMode()
- {
- QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
- settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
- int defaultMode = static_cast<int>(UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_INTERACTION_MODE_DEFAULT);
- int result = settings.value(
- UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_INTERACTION_MODE_KEY,
- defaultMode).toInt();
- settings.endGroup();
- return static_cast<ViewportInteraction::InteractionMode>(result);
- }
- void PersistentSetInteractionMode(ViewportInteraction::InteractionMode mode)
- {
- QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
- settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
- settings.setValue(UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_INTERACTION_MODE_KEY,
- static_cast<int>(mode));
- settings.endGroup();
- }
- ViewportInteraction::CoordinateSystem PersistentGetCoordinateSystem()
- {
- QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
- settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
- int defaultSystem = static_cast<int>(UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_COORDINATE_SYSTEM_DEFAULT);
- int result = settings.value(
- UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_COORDINATE_SYSTEM_KEY,
- defaultSystem).toInt();
- settings.endGroup();
- return static_cast<ViewportInteraction::CoordinateSystem>(result);
- }
- void PersistentSetCoordinateSystem(ViewportInteraction::CoordinateSystem coordinateSystem)
- {
- QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
- settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
- settings.setValue(UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_COORDINATE_SYSTEM_KEY,
- static_cast<int>(coordinateSystem));
- settings.endGroup();
- }
- } // anonymous namespace.
- class ViewportInteractionExpanderWatcher
- : public QObject
- {
- public:
- ViewportInteractionExpanderWatcher(QObject* parent = nullptr)
- : QObject(parent)
- {
- }
- bool eventFilter(QObject* obj, QEvent* event) override
- {
- switch (event->type())
- {
- case QEvent::MouseButtonPress:
- case QEvent::MouseButtonRelease:
- case QEvent::MouseButtonDblClick:
- {
- if (qobject_cast<QToolButton*>(obj))
- {
- auto mouseEvent = static_cast<QMouseEvent*>(event);
- auto expansion = qobject_cast<QToolButton*>(obj);
- expansion->setPopupMode(QToolButton::InstantPopup);
- auto menu = new QMenu(expansion);
- auto toolbar = qobject_cast<QToolBar*>(expansion->parentWidget());
- for (auto toolbarAction : toolbar->actions())
- {
- auto actionWidget = toolbar->widgetForAction(toolbarAction);
- if (actionWidget && !actionWidget->isVisible() && !toolbarAction->text().isEmpty())
- {
- QString plainText = QTextDocumentFragment::fromHtml(actionWidget->toolTip()).toPlainText();
- toolbarAction->setText(plainText);
- menu->addAction(toolbarAction);
- }
- }
- if (menu->actions().count() == 0)
- {
- QAction* noAction = new QAction(this);
- noAction->setEnabled(false);
- noAction->setText(tr("Please resize the toolbar to see all the controls."));
- menu->addAction(noAction);
- }
- menu->exec(mouseEvent->globalPos());
- return true;
- }
- break;
- }
- }
- return QObject::eventFilter(obj, event);
- }
- };
- ViewportInteraction::ViewportInteraction(EditorWindow* editorWindow)
- : QObject()
- , m_editorWindow(editorWindow)
- , m_activeElementId()
- , m_anchorWhole(new ViewportIcon("Editor/Icons/Viewport/Anchor_Whole.tif"))
- , m_pivotIcon(new ViewportIcon("Editor/Icons/Viewport/Pivot.tif"))
- , m_interactionMode(PersistentGetInteractionMode())
- , m_interactionType(InteractionType::NONE)
- , m_coordinateSystem(PersistentGetCoordinateSystem())
- , m_spaceBarIsActive(false)
- , m_leftButtonIsActive(false)
- , m_middleButtonIsActive(false)
- , m_reversibleActionStarted(false)
- , m_startMouseDragPos(0.0f, 0.0f)
- , m_lastMouseDragPos(0.0f, 0.0f)
- , m_selectedElementsAtSelectionStart()
- , m_canvasViewportMatrixProps()
- , m_shouldScaleToFitOnViewportResize(true)
- , m_transformComponentType(AZ::Uuid::CreateNull())
- , m_grabbedEdges(ViewportHelpers::ElementEdges())
- , m_startAnchors(UiTransform2dInterface::Anchors())
- , m_grabbedAnchors(ViewportHelpers::SelectedAnchors())
- , m_grabbedGizmoParts(ViewportHelpers::GizmoParts())
- , m_lineTriangleX(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Line_Triangle_X.tif"))
- , m_lineTriangleY(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Line_Triangle_Y.tif"))
- , m_circle(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Circle.tif"))
- , m_lineSquareX(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Line_Square_X.tif"))
- , m_lineSquareY(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Line_Square_Y.tif"))
- , m_centerSquare(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Center_Square.tif"))
- , m_dottedLine(new ViewportIcon("Editor/Icons/Viewport/DottedLine.tif"))
- , m_dragInteraction(nullptr)
- , m_expanderWatcher(new ViewportInteractionExpanderWatcher(this))
- {
- m_cursorRotate = CMFCUtils::LoadCursor(IDC_POINTER_OBJECT_ROTATE);
- }
- ViewportInteraction::~ViewportInteraction()
- {
- }
- void ViewportInteraction::ClearInteraction(bool clearSpaceBarIsActive)
- {
- m_activeElementId.SetInvalid();
- m_interactionType = InteractionType::NONE;
- m_spaceBarIsActive = clearSpaceBarIsActive ? false : m_spaceBarIsActive;
- m_leftButtonIsActive = false;
- m_middleButtonIsActive = false;
- m_startMouseDragPos = AZ::Vector2::CreateZero();
- m_lastMouseDragPos = AZ::Vector2::CreateZero();
- m_grabbedEdges = ViewportHelpers::ElementEdges();
- m_startAnchors = UiTransform2dInterface::Anchors();
- m_grabbedAnchors = ViewportHelpers::SelectedAnchors();
- m_grabbedGizmoParts = ViewportHelpers::GizmoParts();
- m_selectedElementsAtSelectionStart.clear();
- m_isAreaSelectionActive = false;
- m_reversibleActionStarted = false;
- SAFE_DELETE(m_dragInteraction);
- }
- void ViewportInteraction::Nudge(NudgeDirection direction, NudgeSpeed speed)
- {
- const AZ::Uuid& transformComponentType = InitAndGetTransformComponentType();
- ViewportNudge::Nudge(m_editorWindow,
- m_interactionMode,
- m_editorWindow->GetViewport(),
- direction,
- speed,
- m_editorWindow->GetHierarchy()->selectedItems(),
- m_coordinateSystem,
- transformComponentType);
- }
- void ViewportInteraction::StartObjectPickMode()
- {
- // Temporarily set the viewport interaction mode to "Selection" and disable the toolbar
- m_interactionModeBeforePickMode = GetMode();
- SetMode(InteractionMode::SELECTION);
- m_editorWindow->GetModeToolbar()->setEnabled(false);
- InvalidateHoverElement();
- UpdateCursor();
- }
- void ViewportInteraction::StopObjectPickMode()
- {
- bool mousePressed = GetLeftButtonIsActive();
- m_editorWindow->GetModeToolbar()->setEnabled(true);
- SetMode(m_interactionModeBeforePickMode);
- SetCursorStr("");
- m_hoverElement.SetInvalid();
- // Update interaction type and cursor right away if the mouse is already released (user pressed ESC to cancel pick mode)
- // instead of waiting for a mouse move/release event
- if (!mousePressed)
- {
- QPoint viewportCursorPos = m_editorWindow->GetViewport()->mapFromGlobal(QCursor::pos());
- QTreeWidgetItemRawPtrQList selectedItems = m_editorWindow->GetHierarchy()->selectedItems();
- UpdateInteractionType(AZ::Vector2(aznumeric_cast<float>(viewportCursorPos.x()), aznumeric_cast<float>(viewportCursorPos.y())), selectedItems);
- }
- UpdateCursor();
- }
- bool ViewportInteraction::GetLeftButtonIsActive()
- {
- return m_leftButtonIsActive;
- }
- bool ViewportInteraction::GetSpaceBarIsActive()
- {
- return m_spaceBarIsActive;
- }
- void ViewportInteraction::ActivateSpaceBar()
- {
- m_spaceBarIsActive = true;
- UpdateCursor();
- if (m_editorWindow->GetViewport()->IsInObjectPickMode())
- {
- // Don't highlight the hover element during a pan
- InvalidateHoverElement();
- }
- }
- void ViewportInteraction::Draw(Draw2dHelper& draw2d,
- const QTreeWidgetItemRawPtrQList& selectedItems)
- {
- // Draw border around hover UI element
- if (m_hoverElement.IsValid())
- {
- m_editorWindow->GetViewport()->GetViewportHighlight()->DrawHover(draw2d, m_hoverElement);
- }
- // Draw the guide lines
- if (m_editorWindow->GetViewport()->AreGuidesShown())
- {
- GuideHelpers::DrawGuideLines(m_editorWindow->GetCanvas(), m_editorWindow->GetViewport(), draw2d);
- }
- // Draw the transform gizmo where appropriate
- if (m_interactionMode != InteractionMode::SELECTION)
- {
- LyShine::EntityArray selectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
- switch (m_interactionMode)
- {
- case InteractionMode::MOVE:
- case InteractionMode::ANCHOR:
- for (auto element : selectedElements)
- {
- if (!ViewportHelpers::IsControlledByLayout(element))
- {
- DrawAxisGizmo(draw2d, element, m_coordinateSystem, m_lineTriangleX.get(), m_lineTriangleY.get());
- }
- }
- break;
- case InteractionMode::ROTATE:
- for (auto element : selectedElements)
- {
- DrawCircleGizmo(draw2d, element);
- }
- break;
- case InteractionMode::RESIZE:
- for (auto element : selectedElements)
- {
- if (!ViewportHelpers::IsControlledByLayout(element))
- {
- DrawAxisGizmo(draw2d, element, CoordinateSystem::LOCAL, m_lineSquareX.get(), m_lineSquareY.get());
- }
- }
- break;
- }
- }
- // Draw the area selection, if there is one
- if (AreaSelectionIsActive())
- {
- m_dottedLine->DrawAxisAlignedBoundingBox(draw2d, m_startMouseDragPos, m_lastMouseDragPos);
- }
- // If there is an active drag interaction give it a chance to render its interaction display
- if (m_dragInteraction)
- {
- m_dragInteraction->Render(draw2d);
- }
- // Draw the cursor string
- if (!m_cursorStr.empty() && m_editorWindow->GetViewport()->underMouse())
- {
- ViewportHelpers::DrawCursorText(m_cursorStr, draw2d, m_editorWindow->GetViewport());
- }
- }
- bool ViewportInteraction::AreaSelectionIsActive()
- {
- return m_isAreaSelectionActive;
- }
- void ViewportInteraction::BeginReversibleAction(const QTreeWidgetItemRawPtrQList& selectedItems)
- {
- if ((m_reversibleActionStarted) ||
- (m_interactionType == InteractionType::NONE || m_interactionType == InteractionType::GUIDE) ||
- (m_interactionMode == InteractionMode::SELECTION))
- {
- // Nothing to do.
- return;
- }
- // we are about to change something and we have not started an undo action yet, start one
- m_reversibleActionStarted = true;
- // Tell the Properties panel that we're about to do a reversible action
- HierarchyClipboard::BeginUndoableEntitiesChange(m_editorWindow, m_selectedEntitiesUndoState);
- // Snapping.
- bool isSnapping = false;
- UiEditorCanvasBus::EventResult(isSnapping, m_editorWindow->GetCanvas(), &UiEditorCanvasBus::Events::GetIsSnapEnabled);
- if (isSnapping)
- {
- // Set all initial non-snapped values.
- HierarchyItemRawPtrList items = SelectionHelpers::GetSelectedHierarchyItems(m_editorWindow->GetHierarchy(),
- selectedItems);
- for (auto i : items)
- {
- UiTransform2dInterface::Offsets offsets;
- UiTransform2dBus::EventResult(offsets, i->GetEntityId(), &UiTransform2dBus::Events::GetOffsets);
- i->SetNonSnappedOffsets(offsets);
- float rotation;
- UiTransformBus::EventResult(rotation, i->GetEntityId(), &UiTransformBus::Events::GetZRotation);
- i->SetNonSnappedZRotation(rotation);
- }
- }
- }
- void ViewportInteraction::EndReversibleAction()
- {
- if (!m_reversibleActionStarted)
- {
- // Nothing to do.
- return;
- }
- m_reversibleActionStarted = false;
- if (AreaSelectionIsActive())
- {
- // Nothing to do.
- return;
- }
- // Note that EndReversibleAction is not used for interactions that handle undo in a m_dragInteraction
- // Ideally we will change them all to use m_dragInteraction and handle the undo there.
- HierarchyClipboard::EndUndoableEntitiesChange(m_editorWindow, "viewport interaction", m_selectedEntitiesUndoState);
- }
- void ViewportInteraction::MousePressEvent(QMouseEvent* ev)
- {
- AZ::Vector2 mousePosition = QtHelpers::QPointFToVector2(ev->localPos());
- m_startMouseDragPos = m_lastMouseDragPos = mousePosition;
- const bool ctrlKeyPressed = ev->modifiers().testFlag(Qt::ControlModifier);
- // Detect whether an entity was picked on the mouse press so that
- // mouse move/release events can be handled appropriately
- m_entityPickedOnMousePress = false;
- // Prepare to handle panning
- if ((!m_leftButtonIsActive) &&
- (ev->button() == Qt::MiddleButton))
- {
- m_middleButtonIsActive = true;
- }
- else if ((!m_middleButtonIsActive) &&
- (ev->button() == Qt::LeftButton))
- {
- // Prepare for clicking and dragging
- m_leftButtonIsActive = true;
- if (m_activeElementId.IsValid())
- {
- if (m_grabbedAnchors.Any())
- {
- // Prepare to move anchors
- UiTransform2dBus::EventResult(m_startAnchors, m_activeElementId, &UiTransform2dBus::Events::GetAnchors);
- }
- else if (m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR)
- {
- // Prepare for moving elements by offsets or anchors
- QTreeWidgetItemRawPtrQList selectedItems = m_editorWindow->GetHierarchy()->selectedItems();
- m_dragInteraction = new ViewportMoveInteraction(m_editorWindow->GetHierarchy(), selectedItems,
- m_editorWindow->GetCanvas(), GetActiveElement(), m_coordinateSystem, m_grabbedGizmoParts, m_interactionMode, m_interactionType, mousePosition);
- }
- }
- else if (m_interactionType == InteractionType::GUIDE)
- {
- // We are hovering over a guide with the move guide icon displayed so start the move guide interaction
- m_dragInteraction = new ViewportMoveGuideInteraction(m_editorWindow, m_editorWindow->GetCanvas(),
- m_activeGuideIsVertical, m_activeGuideIndex, mousePosition);
- }
- }
- // If there isn't another interaction happening, try to select an element
- if ((!m_spaceBarIsActive && !m_middleButtonIsActive && m_interactionType == InteractionType::NONE) ||
- ((m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR) && m_interactionType == InteractionType::DIRECT && ctrlKeyPressed))
- {
- if (m_editorWindow->GetViewport()->IsInObjectPickMode())
- {
- AZ::Entity* element = nullptr;
- UiCanvasBus::EventResult(element, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::PickElement, mousePosition);
- m_editorWindow->GetViewport()->PickItem(element ? element->GetId() : AZ::EntityId());
- m_entityPickedOnMousePress = true;
- }
- else
- {
- QTreeWidgetItemRawPtrQList selectedItems = m_editorWindow->GetHierarchy()->selectedItems();
- // Because we draw the Anchors (grayed out) in MOVE mode or when multiple items are selected in ANCHOR mode then it is confusing if you
- // click on them, thinking it might do something, and it changes the selection.
- // But if the click is inside the element that the anchor belongs to we do want to consider it a select or it would get in the way.
- // So a compromise is that, if you click on them, and the click is outside the element they belong to, then the click is ignored.
- bool ignoreClickForSelection = false;
- if ((m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR) && m_interactionType == InteractionType::NONE)
- {
- LyShine::EntityArray topLevelSelectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
- for (auto elementWithAnchors : topLevelSelectedElements)
- {
- ViewportHelpers::SelectedAnchors grabbedAnchors;
- if (!ViewportHelpers::IsControlledByLayout(elementWithAnchors) &&
- ViewportElement::PickAnchors(elementWithAnchors, mousePosition, m_anchorWhole->GetTextureSize(), grabbedAnchors))
- {
- // Hovering over anchors, if the click is outside the element with the anchors then ignore
- bool isElementUnderCursor = false;
- UiTransformBus::EventResult(
- isElementUnderCursor, elementWithAnchors->GetId(), &UiTransformBus::Events::IsPointInRect, mousePosition);
- if (!isElementUnderCursor)
- {
- ignoreClickForSelection = true;
- break;
- }
- }
- }
- }
- if (!ignoreClickForSelection)
- {
- AZ::Entity* element = nullptr;
- UiCanvasBus::EventResult(element, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::PickElement, mousePosition);
- HierarchyWidget* hierarchyWidget = m_editorWindow->GetHierarchy();
- QTreeWidgetItem* widgetItem = nullptr;
- bool itemDeselected = false;
- // store the selected items at the start of the selection
- m_selectedElementsAtSelectionStart = SelectionHelpers::GetSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
- if (element)
- {
- widgetItem = HierarchyHelpers::ElementToItem(hierarchyWidget, element, false);
- // If user is selecting something with the control key pressed, the
- // element may need to be de-selected (if it's already selected).
- itemDeselected = HierarchyHelpers::HandleDeselect(widgetItem, ctrlKeyPressed);
- }
- // If the item didn't need to be de-selected, then we should select it
- if (!itemDeselected)
- {
- // Note that widgetItem could still be null at this point, but
- // SetSelectedItem will handle this situation for us.
- HierarchyHelpers::SetSelectedItem(hierarchyWidget, element);
- }
- // ClearInteraction gets called if the selection changes to empty but we do not want to clear these since we can start a drag now
- m_leftButtonIsActive = true;
- m_startMouseDragPos = m_lastMouseDragPos = mousePosition;
- m_isAreaSelectionActive = true;
- }
- }
- }
- UpdateCursor();
- }
- void ViewportInteraction::PanOnMouseMoveEvent(const AZ::Vector2& mousePosition)
- {
- AZ::Vector2 deltaPosition = mousePosition - m_lastMouseDragPos;
- AZ::Vector3 mousePosDelta(EntityHelpers::MakeVec3(deltaPosition));
- m_canvasViewportMatrixProps.translation += mousePosDelta;
- UpdateCanvasToViewportMatrix();
- UpdateShouldScaleToFitOnResize();
- }
- const AZ::Uuid& ViewportInteraction::InitAndGetTransformComponentType()
- {
- if (m_transformComponentType.IsNull())
- {
- m_transformComponentType = LyShine::UiTransform2dComponentUuid;
- }
- return m_transformComponentType;
- }
- void ViewportInteraction::MouseMoveEvent(QMouseEvent* ev,
- const QTreeWidgetItemRawPtrQList& selectedItems)
- {
- AZ::Vector2 mousePosition = QtHelpers::QPointFToVector2(ev->localPos());
- if (m_spaceBarIsActive)
- {
- if (m_leftButtonIsActive || m_middleButtonIsActive)
- {
- PanOnMouseMoveEvent(mousePosition);
- }
- }
- else if (m_leftButtonIsActive)
- {
- if (!m_entityPickedOnMousePress)
- {
- // Click and drag
- ProcessInteraction(mousePosition,
- ev->modifiers(),
- selectedItems);
- }
- }
- else if (m_middleButtonIsActive)
- {
- PanOnMouseMoveEvent(mousePosition);
- }
- else if (ev->buttons() == Qt::NoButton)
- {
- // Hover
- if (m_editorWindow->GetViewport()->IsInObjectPickMode())
- {
- // Update hover element. We only display the hover element in object pick mode
- UpdateHoverElement(mousePosition);
- }
- else
- {
- m_interactionType = InteractionType::NONE;
- m_grabbedEdges.SetAll(false);
- m_grabbedAnchors.SetAll(false);
- m_grabbedGizmoParts.SetBoth(false);
- UpdateInteractionType(mousePosition,
- selectedItems);
- UpdateCursor();
- }
- }
- m_lastMouseDragPos = mousePosition;
- }
- void ViewportInteraction::MouseReleaseEvent(QMouseEvent* ev,
- [[maybe_unused]] const QTreeWidgetItemRawPtrQList& selectedItems)
- {
- if (!m_entityPickedOnMousePress)
- {
- // if the mouse press and release were in the same position and
- // no changes have been made then we can treat it as a mouse-click which can
- // do selection. This is useful in the case where we are in move mode but just
- // clicked on something that is either:
- // - one of multiple things selected and we want to just select this
- // - an element in front of something that is selected
- // In this case the mouse press will not have been treated as selection in
- // MousePressEvent so we need to handle this as a special case
- if (!m_reversibleActionStarted && m_lastMouseDragPos == m_startMouseDragPos &&
- ev->button() == Qt::LeftButton && !ev->modifiers().testFlag(Qt::ControlModifier) &&
- (m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR)
- && (m_interactionType == InteractionType::DIRECT || m_interactionType == InteractionType::TRANSFORM_GIZMO))
- {
- AZ::Vector2 mousePosition = QtHelpers::QPointFToVector2(ev->localPos());
- bool ignoreClick = false;
- if (m_interactionType == InteractionType::TRANSFORM_GIZMO)
- {
- // if we clicked on a gizmo but didn't move then we want to consider this a select click as long as the click
- // was inside the active element. (the square part of the gizmo can cover a large area of the element so ignoring
- // the click is confusing).
- if (m_activeElementId.IsValid())
- {
- bool isActiveElementUnderCursor = false;
- UiTransformBus::EventResult(
- isActiveElementUnderCursor, m_activeElementId, &UiTransformBus::Events::IsPointInRect, mousePosition);
- if (!isActiveElementUnderCursor)
- {
- ignoreClick = true;
- }
- }
- }
- if (!ignoreClick)
- {
- AZ::Entity* element = nullptr;
- UiCanvasBus::EventResult(element, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::PickElement, mousePosition);
- HierarchyWidget* hierarchyWidget = m_editorWindow->GetHierarchy();
- if (element)
- {
- HierarchyHelpers::SetSelectedItem(hierarchyWidget, element);
- }
- }
- }
- if (m_dragInteraction)
- {
- // test to see if the mouse position is inside the viewport on each axis
- const QPoint& pos = ev->pos();
- const AZ::Vector2 size = m_editorWindow->GetViewport()->GetRenderViewportSize();
- ViewportDragInteraction::EndState inWidget;
- if (pos.x() >= 0 && pos.x() < size.GetX())
- inWidget = pos.y() >= 0 && pos.y() < size.GetY() ? ViewportDragInteraction::EndState::Inside : ViewportDragInteraction::EndState::OutsideY;
- else
- inWidget = pos.y() >= 0 && pos.y() < size.GetY() ? ViewportDragInteraction::EndState::OutsideX : ViewportDragInteraction::EndState::OutsideXY;
- // Some interactions end differently depending on whether the mouse was released inside or outside the viewport
- m_dragInteraction->EndInteraction(inWidget);
- }
- // Tell the Properties panel to update.
- // Refresh attributes as well in case this change affects an attribute (ex. anchors affect warning text on scale to device mode)
- const AZ::Uuid& transformComponentType = InitAndGetTransformComponentType();
- m_editorWindow->GetProperties()->TriggerRefresh(AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues, &transformComponentType);
- // Tell the Properties panel that the reversible action is complete
- EndReversibleAction();
- }
- // Reset the interaction
- ClearInteraction(false);
- if (!m_spaceBarIsActive)
- {
- // Immediately update the interaction type and cursor (using the possibly new selection)
- AZ::Vector2 mousePosition = QtHelpers::QPointFToVector2(ev->localPos());
- UpdateInteractionType(mousePosition,
- m_editorWindow->GetHierarchy()->selectedItems());
- }
- UpdateCursor();
- }
- bool ViewportInteraction::MouseWheelEvent(QWheelEvent* ev)
- {
- if (m_leftButtonIsActive || m_middleButtonIsActive)
- {
- // Ignore event.
- return false;
- }
- const QPoint numDegrees(ev->angleDelta());
- if (!numDegrees.isNull())
- {
- // Angle delta returns distance rotated by mouse wheel in eigths of a
- // degree.
- static const int numStepsPerDegree = 8;
- const float numScrollDegrees = aznumeric_cast<float>(numDegrees.y() / numStepsPerDegree);
- static const float zoomMultiplier = 1 / 100.0f;
-
- float newScale = m_canvasViewportMatrixProps.scale + numScrollDegrees * zoomMultiplier;
- SetCanvasToViewportScale(QuantizeZoomScale(newScale), AZ::Vector2(ev->position().x(), ev->position().y()));
- }
- return true;
- }
- bool ViewportInteraction::KeyPressEvent(QKeyEvent* ev)
- {
- switch (ev->key())
- {
- case Qt::Key_Space:
- if (!ev->isAutoRepeat())
- {
- ActivateSpaceBar();
- }
- return true;
- case Qt::Key_Up:
- Nudge(ViewportInteraction::NudgeDirection::Up,
- (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
- return true;
- case Qt::Key_Down:
- Nudge(ViewportInteraction::NudgeDirection::Down,
- (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
- return true;
- case Qt::Key_Left:
- Nudge(ViewportInteraction::NudgeDirection::Left,
- (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
- return true;
- case Qt::Key_Right:
- Nudge(ViewportInteraction::NudgeDirection::Right,
- (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
- return true;
- default:
- break;
- }
- return false;
- }
- bool ViewportInteraction::KeyReleaseEvent(QKeyEvent* ev)
- {
- if (ev->key() == Qt::Key_Space)
- {
- if (!ev->isAutoRepeat())
- {
- ClearInteraction();
- UpdateCursor();
- if (m_editorWindow->GetViewport()->IsInObjectPickMode())
- {
- // Update hover element right away in case mouse is over an element
- QPoint viewportCursorPos = m_editorWindow->GetViewport()->mapFromGlobal(QCursor::pos());
- UpdateHoverElement(AZ::Vector2(aznumeric_cast<float>(viewportCursorPos.x()), aznumeric_cast<float>(viewportCursorPos.y())));
- }
- }
- return true;
- }
- return false;
- }
- void ViewportInteraction::SetMode(InteractionMode m)
- {
- ClearInteraction();
- m_interactionMode = m;
- PersistentSetInteractionMode(m_interactionMode);
- m_editorWindow->GetModeToolbar()->SetCheckedItem(static_cast<int>(m_interactionMode));
- m_editorWindow->GetModeToolbar()->GetAlignToolbarSection()->SetIsVisible(
- m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR);
- UpdateCoordinateSystemToolbarSection();
- m_editorWindow->GetViewport()->Refresh();
- }
- ViewportInteraction::InteractionMode ViewportInteraction::GetMode() const
- {
- return m_interactionMode;
- }
- ViewportInteraction::InteractionType ViewportInteraction::GetInteractionType() const
- {
- return m_interactionType;
- }
- void ViewportInteraction::SetCoordinateSystem(CoordinateSystem s)
- {
- m_coordinateSystem = s;
- PersistentSetCoordinateSystem(s);
- m_editorWindow->GetCoordinateSystemToolbarSection()->SetCurrentIndex(static_cast<int>(m_coordinateSystem));
- m_editorWindow->GetViewport()->Refresh();
- }
- ViewportInteraction::CoordinateSystem ViewportInteraction::GetCoordinateSystem() const
- {
- return m_coordinateSystem;
- }
- void ViewportInteraction::InitializeToolbars()
- {
- m_editorWindow->GetModeToolbar()->SetCheckedItem(static_cast<int>(m_interactionMode));
- m_editorWindow->GetModeToolbar()->GetAlignToolbarSection()->SetIsVisible(m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR);
- m_editorWindow->GetCoordinateSystemToolbarSection()->SetCurrentIndex(static_cast<int>(m_coordinateSystem));
- UpdateCoordinateSystemToolbarSection();
- bool canvasLoaded = m_editorWindow->GetCanvas().IsValid();
- m_editorWindow->GetMainToolbar()->setEnabled(canvasLoaded);
- m_editorWindow->GetModeToolbar()->setEnabled(canvasLoaded);
- if (!m_editorWindow->GetModeToolbar()->isEnabled())
- {
- m_editorWindow->GetCoordinateSystemToolbarSection()->SetIsEnabled(false);
- }
- m_editorWindow->GetEnterPreviewToolbar()->setEnabled(canvasLoaded);
- AZ::Vector2 canvasSize(1280.0f, 720.0f);
- UiCanvasBus::EventResult(canvasSize, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::GetCanvasSize);
- m_editorWindow->GetCanvasSizeToolbarSection()->SetInitialResolution(canvasSize);
- if (!m_editorWindow->GetCanvas().IsValid())
- {
- SetCanvasToViewportScale(1.0f);
- }
- {
- bool isSnapping = false;
- UiEditorCanvasBus::EventResult(isSnapping, m_editorWindow->GetCanvas(), &UiEditorCanvasBus::Events::GetIsSnapEnabled);
- m_editorWindow->GetCoordinateSystemToolbarSection()->SetSnapToGridIsChecked(isSnapping);
- }
- if (QToolButton* expansion = AzQtComponents::ToolBar::getToolBarExpansionButton(m_editorWindow->GetMainToolbar()))
- {
- expansion->installEventFilter(m_expanderWatcher);
- }
- if (QToolButton* expansion = AzQtComponents::ToolBar::getToolBarExpansionButton(m_editorWindow->GetModeToolbar()))
- {
- expansion->installEventFilter(m_expanderWatcher);
- }
- if (QToolButton* expansion = AzQtComponents::ToolBar::getToolBarExpansionButton(m_editorWindow->GetPreviewToolbar()))
- {
- expansion->installEventFilter(m_expanderWatcher);
- }
- if (QToolButton* expansion = AzQtComponents::ToolBar::getToolBarExpansionButton(m_editorWindow->GetEnterPreviewToolbar()))
- {
- expansion->installEventFilter(m_expanderWatcher);
- }
- }
- const ViewportInteraction::TranslationAndScale& ViewportInteraction::GetCanvasViewportMatrixProps()
- {
- return m_canvasViewportMatrixProps;
- }
- void ViewportInteraction::SetCanvasViewportMatrixProps(const TranslationAndScale& canvasViewportMatrixProps)
- {
- m_canvasViewportMatrixProps = canvasViewportMatrixProps;
- UpdateCanvasToViewportMatrix();
- UpdateShouldScaleToFitOnResize();
- }
- void ViewportInteraction::CenterCanvasInViewport(const AZ::Vector2* newCanvasSize)
- {
- GetScaleToFitTransformProps(newCanvasSize, m_canvasViewportMatrixProps);
- // Apply scale and translation changes
- UpdateCanvasToViewportMatrix();
- m_shouldScaleToFitOnViewportResize = true;
- }
- void ViewportInteraction::GetScaleToFitTransformProps(const AZ::Vector2* newCanvasSize, TranslationAndScale& propsOut)
- {
- AZ::Vector2 canvasSize;
- // Normally we can just get the canvas size from GetCanvasSize, but if the canvas
- // size was recently changed, the caller can choose to provide a new canvas size
- // so we don't have to wait for the canvas size to update.
- if (newCanvasSize)
- {
- canvasSize = *newCanvasSize;
- }
- else
- {
- UiCanvasBus::EventResult(canvasSize, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::GetCanvasSize);
- }
- AZ::Vector2 viewportSize = m_editorWindow->GetViewport()->GetRenderViewportSize();
- const float viewportWidth = viewportSize.GetX();
- const float viewportHeight = viewportSize.GetY();
- // We pad the edges of the viewport to allow the user to easily see the borders of
- // the canvas edges, which is especially helpful if there are anchors sitting on
- // the edges of the canvas.
- static const int canvasBorderPaddingInPixels = 32;
- AZ::Vector2 viewportPaddedSize(
- viewportWidth - canvasBorderPaddingInPixels,
- viewportHeight - canvasBorderPaddingInPixels);
- // Guard against very small viewports
- if (viewportPaddedSize.GetX() <= 0.0f)
- {
- viewportPaddedSize.SetX(viewportWidth);
- }
- if (viewportPaddedSize.GetY() <= 0.0f)
- {
- viewportPaddedSize.SetY(viewportHeight);
- }
- // Use a "scale to fit" approach
- const float canvasToViewportScale = AZ::GetMin<float>(
- viewportPaddedSize.GetX() / canvasSize.GetX(),
- viewportPaddedSize.GetY() / canvasSize.GetY());
- const float scaledCanvasWidth = canvasSize.GetX() * canvasToViewportScale;
- const float scaledCanvasHeight = canvasSize.GetY() * canvasToViewportScale;
- // Centers the canvas within the viewport
- propsOut.translation = AZ::Vector3(
- 0.5f * (viewportWidth - scaledCanvasWidth),
- 0.5f * (viewportHeight - scaledCanvasHeight),
- 0.0f);
- propsOut.scale = canvasToViewportScale;
- }
- void ViewportInteraction::DecreaseCanvasToViewportScale()
- {
- SetCanvasToViewportScale(QuantizeZoomScale(m_canvasViewportMatrixProps.scale - defaultCanvasToViewportScaleIncrement));
- }
- void ViewportInteraction::IncreaseCanvasToViewportScale()
- {
- SetCanvasToViewportScale(QuantizeZoomScale(m_canvasViewportMatrixProps.scale + defaultCanvasToViewportScaleIncrement));
- }
- void ViewportInteraction::ResetCanvasToViewportScale()
- {
- SetCanvasToViewportScale(1.0f);
- }
- void ViewportInteraction::SetCanvasZoomPercent(float percent)
- {
- SetCanvasToViewportScale(percent / 100.0f);
- }
- void ViewportInteraction::SetCanvasToViewportScale(float newScale, const AZStd::optional<AZ::Vector2>& pivotPoint)
- {
- // only allow setting the viewport scale if current window is active
- // OnTick for ViewportWidget is needed to reevaluate the layout, but does not happen when the window loses focus
- if (!m_editorWindow->isActiveWindow())
- {
- return;
- }
- static const float minZoom = 0.1f;
- static const float maxZoom = 10.0f;
- const float currentScale = m_canvasViewportMatrixProps.scale;
- m_canvasViewportMatrixProps.scale = AZ::GetClamp(newScale, minZoom, maxZoom);
- if (m_editorWindow->GetCanvas().IsValid())
- {
- // Pivot the zoom based off the center of the viewport's location in canvas space
- // Calculate diff between the number of viewport pixels occupied by the current
- // scaled canvas view and the new one
- AZ::Vector2 canvasSize;
- UiCanvasBus::EventResult(canvasSize, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::GetCanvasSize);
- const AZ::Vector2 scaledCanvasSize(canvasSize * currentScale);
- const AZ::Vector2 newScaledCanvasSize(canvasSize * m_canvasViewportMatrixProps.scale);
- const AZ::Vector2 scaledCanvasSizeDiff(newScaledCanvasSize - scaledCanvasSize);
- // Get the distance between our pivot point and the upper-left corner of the
- // canvas (in viewport space)
- const AZ::Vector2 canvasUpperLeft(m_canvasViewportMatrixProps.translation);
- // Use the center of our viewport as the pivot point if a pivot is not provided
- const AZ::Vector2 pivotDiff = canvasUpperLeft - pivotPoint.value_or(([&]{
- const AZ::Vector2 viewportSize = m_editorWindow->GetViewport()->GetRenderViewportSize();
- return viewportSize * AZ::Vector2(0.5f, 0.5f);
- })());
- // Calculate the pivot position relative to the current scaled canvas size. For
- // example, if the pivot position is the upper-left corner of the canvas, this
- // will be (0, 0), whereas if the pivot position is the bottom-right corner of
- // the canvas, this will be (1, 1).
- AZ::Vector2 relativePivotPosition(
- pivotDiff.GetX() / scaledCanvasSize.GetX(),
- pivotDiff.GetY() / scaledCanvasSize.GetY());
- // Use the relative pivot position to essentially determine what percentage of
- // the difference between the two on-screen canvas sizes should be used to move
- // the canvas by to pivot the zoom. For example, if the pivot position is the
- // bottom-right corner of the canvas, then we will use 100% of the difference
- // in on-screen canvas sizes to move the canvas right and up (to maintain the
- // view of the bottom-right corner).
- AZ::Vector2 pivotTranslation(
- scaledCanvasSizeDiff.GetX() * relativePivotPosition.GetX(),
- scaledCanvasSizeDiff.GetY() * relativePivotPosition.GetY());
- m_canvasViewportMatrixProps.translation.SetX(m_canvasViewportMatrixProps.translation.GetX() + pivotTranslation.GetX());
- m_canvasViewportMatrixProps.translation.SetY(m_canvasViewportMatrixProps.translation.GetY() + pivotTranslation.GetY());
- }
- UpdateCanvasToViewportMatrix();
- UpdateShouldScaleToFitOnResize();
- }
- float ViewportInteraction::QuantizeZoomScale(float newScale)
- {
- // Fit to canvas can result in odd zoom scales - when manually zooming we snap it to one of the prefered intervals
- // The prefered intervals are in steps of defaultCanvasToViewportScaleIncrement starting at 100% (or a scale of 1.0)
- float scaleRelativeto1 = newScale - 1.0f;
- float roundedRelative = round(scaleRelativeto1 / defaultCanvasToViewportScaleIncrement) * defaultCanvasToViewportScaleIncrement;
- float quantizedScale = roundedRelative + 1.0f;
- return quantizedScale;
- }
- void ViewportInteraction::UpdateZoomFactorLabel()
- {
- float percentage = m_canvasViewportMatrixProps.scale * 100.0f;
- m_editorWindow->GetMainToolbar()->SetZoomPercent(percentage);
- }
- AZ::Entity* ViewportInteraction::GetActiveElement() const
- {
- return EntityHelpers::GetEntity(m_activeElementId);
- }
- const AZ::EntityId& ViewportInteraction::GetActiveElementId() const
- {
- return m_activeElementId;
- }
- ViewportHelpers::SelectedAnchors ViewportInteraction::GetGrabbedAnchors() const
- {
- return m_grabbedAnchors;
- }
- void ViewportInteraction::UpdateInteractionType(const AZ::Vector2& mousePosition,
- const QTreeWidgetItemRawPtrQList& selectedItems)
- {
- switch (m_interactionMode)
- {
- case InteractionMode::MOVE:
- case InteractionMode::ANCHOR:
- {
- auto selectedElements = SelectionHelpers::GetSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
- if (selectedElements.size() == 1 && m_interactionMode == InteractionMode::ANCHOR)
- {
- auto selectedElement = selectedElements.front();
- if (!ViewportHelpers::IsControlledByLayout(selectedElement) &&
- ViewportElement::PickAnchors(selectedElement, mousePosition, m_anchorWhole->GetTextureSize(), m_grabbedAnchors))
- {
- // Hovering over anchors
- m_interactionType = InteractionType::ANCHORS;
- m_activeElementId = selectedElement->GetId();
- return;
- }
- }
- auto topLevelSelectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
- for (auto element : topLevelSelectedElements)
- {
- if (!ViewportHelpers::IsControlledByLayout(element) &&
- ViewportElement::PickAxisGizmo(element, m_coordinateSystem, m_interactionMode, mousePosition, m_lineTriangleX->GetTextureSize(), m_grabbedGizmoParts))
- {
- // Hovering over move gizmo
- m_interactionType = InteractionType::TRANSFORM_GIZMO;
- m_activeElementId = element->GetId();
- return;
- }
- }
- // if hovering over a guide line, then allow moving it or deleting it by moving out of viewport
- if (m_editorWindow->GetViewport()->AreGuidesShown() && !GuideHelpers::AreGuidesLocked(m_editorWindow->GetCanvas()))
- {
- if (GuideHelpers::PickGuide(m_editorWindow->GetCanvas(), mousePosition, m_activeGuideIsVertical, m_activeGuideIndex))
- {
- m_interactionType = InteractionType::GUIDE;
- m_activeElementId.SetInvalid();
- return;
- }
- }
- for (auto element : selectedElements)
- {
- bool isElementUnderCursor = false;
- UiTransformBus::EventResult(isElementUnderCursor, element->GetId(), &UiTransformBus::Events::IsPointInRect, mousePosition);
- if (isElementUnderCursor)
- {
- // Hovering over a selected element
- m_interactionType = InteractionType::DIRECT;
- m_activeElementId = element->GetId();
- return;
- }
- }
- break;
- }
- case InteractionMode::ROTATE:
- {
- auto topLevelSelectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
- for (auto element : topLevelSelectedElements)
- {
- if (ViewportElement::PickPivot(element, mousePosition, m_pivotIcon->GetTextureSize()))
- {
- // Hovering over pivot
- m_interactionType = InteractionType::PIVOT;
- m_activeElementId = element->GetId();
- return;
- }
- }
- for (auto element : topLevelSelectedElements)
- {
- if (ViewportElement::PickCircleGizmo(element, mousePosition, m_circle->GetTextureSize(), m_grabbedGizmoParts))
- {
- // Hovering over rotate gizmo
- m_interactionType = InteractionType::TRANSFORM_GIZMO;
- m_activeElementId = element->GetId();
- return;
- }
- }
- break;
- }
- case InteractionMode::RESIZE:
- {
- auto topLevelSelectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
- for (auto element : topLevelSelectedElements)
- {
- if (!ViewportHelpers::IsControlledByLayout(element) &&
- ViewportElement::PickAxisGizmo(element, CoordinateSystem::LOCAL, m_interactionMode, mousePosition, m_lineTriangleX->GetTextureSize(), m_grabbedGizmoParts))
- {
- // Hovering over resize gizmo
- m_interactionType = InteractionType::TRANSFORM_GIZMO;
- m_activeElementId = element->GetId();
- return;
- }
- }
- auto selectedElements = SelectionHelpers::GetSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
- for (auto element : selectedElements)
- {
- if (!ViewportHelpers::IsControlledByLayout(element))
- {
- // Check for grabbing element edges
- ViewportElement::PickElementEdges(element, mousePosition, g_elementEdgeForgiveness, m_grabbedEdges);
- if (m_grabbedEdges.BothHorizontal() || m_grabbedEdges.BothVertical())
- {
- // Don't grab both opposite edges
- m_grabbedEdges.SetAll(false);
- }
- if (m_grabbedEdges.Any())
- {
- m_interactionType = InteractionType::DIRECT;
- m_activeElementId = element->GetId();
- return;
- }
- }
- }
- break;
- }
- default:
- {
- // Do nothing
- break;
- }
- } // switch statement
- }
- void ViewportInteraction::UpdateCursor()
- {
- QCursor cursor = Qt::ArrowCursor;
- if (m_spaceBarIsActive)
- {
- cursor = (m_leftButtonIsActive || m_middleButtonIsActive ? Qt::ClosedHandCursor : Qt::OpenHandCursor);
- }
- else if (m_interactionType == InteractionType::GUIDE)
- {
- if (m_activeGuideIsVertical)
- {
- cursor = Qt::SplitHCursor; // vertical guide
- }
- else
- {
- cursor = Qt::SplitVCursor; // horizontal guide
- }
- }
- else if (m_activeElementId.IsValid())
- {
- if ((m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR) &&
- m_interactionType == InteractionType::DIRECT)
- {
- cursor = Qt::SizeAllCursor;
- }
- else if (m_interactionMode == InteractionMode::ROTATE &&
- m_interactionType == InteractionType::TRANSFORM_GIZMO)
- {
- cursor = m_cursorRotate;
- }
- else if (m_interactionMode == InteractionMode::RESIZE &&
- m_interactionType == InteractionType::DIRECT)
- {
- UiTransformInterface::RectPoints rect;
- UiTransformBus::Event(m_activeElementId, &UiTransformBus::Events::GetViewportSpacePoints, rect);
- float topAngle = RAD2DEG(atan2f(rect.TopRight().GetY() - rect.TopLeft().GetY(), rect.TopRight().GetX() - rect.TopLeft().GetX()));
- float leftAngle = RAD2DEG(atan2f(rect.TopLeft().GetY() - rect.BottomLeft().GetY(), rect.TopLeft().GetX() - rect.BottomLeft().GetX()));
- float topLeftAngle = 0.5f * (topAngle + leftAngle);
- float topRightAngle = ViewportHelpers::GetPerpendicularAngle(topLeftAngle);
- if (m_grabbedEdges.TopLeft() || m_grabbedEdges.BottomRight())
- {
- cursor = ViewportHelpers::GetSizingCursor(topLeftAngle);
- }
- else if (m_grabbedEdges.TopRight() || m_grabbedEdges.BottomLeft())
- {
- cursor = ViewportHelpers::GetSizingCursor(topRightAngle);
- }
- else if (m_grabbedEdges.m_left || m_grabbedEdges.m_right)
- {
- cursor = ViewportHelpers::GetSizingCursor(leftAngle);
- }
- else if (m_grabbedEdges.m_top || m_grabbedEdges.m_bottom)
- {
- cursor = ViewportHelpers::GetSizingCursor(topAngle);
- }
- }
- }
- else if (m_editorWindow->GetViewport()->IsInObjectPickMode())
- {
- cursor = m_editorWindow->GetEntityPickerCursor();
- }
- m_editorWindow->GetViewport()->setCursor(cursor);
- }
- void ViewportInteraction::UpdateHoverElement(const AZ::Vector2 mousePosition)
- {
- m_hoverElement.SetInvalid();
- AZ::Entity* element = nullptr;
- UiCanvasBus::EventResult(element, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::PickElement, mousePosition);
- if (element)
- {
- m_hoverElement = element->GetId();
- SetCursorStr(element->GetName());
- }
- else
- {
- SetCursorStr("");
- }
- }
- void ViewportInteraction::InvalidateHoverElement()
- {
- m_hoverElement.SetInvalid();
- SetCursorStr("");
- }
- void ViewportInteraction::SetCursorStr(const AZStd::string& cursorStr)
- {
- m_cursorStr = cursorStr;
- }
- void ViewportInteraction::UpdateCanvasToViewportMatrix()
- {
- AZ::Vector3 scaleVec3(m_canvasViewportMatrixProps.scale, m_canvasViewportMatrixProps.scale, 1.0f);
- AZ::Matrix4x4 updatedMatrix = AZ::Matrix4x4::CreateScale(scaleVec3);
- updatedMatrix.SetTranslation(m_canvasViewportMatrixProps.translation);
- UiCanvasBus::Event(m_editorWindow->GetCanvas(), &UiCanvasBus::Events::SetCanvasToViewportMatrix, updatedMatrix);
- UpdateZoomFactorLabel();
- // when the zoom or pan changes we need to redraw the rulers
- m_editorWindow->GetViewport()->RefreshRulers();
- }
- void ViewportInteraction::UpdateShouldScaleToFitOnResize()
- {
- // If the current viewport matrix props match the "scale to fit" props,
- // the canvas will scale to fit when the viewport resizes.
- TranslationAndScale props;
- GetScaleToFitTransformProps(nullptr, props);
- m_shouldScaleToFitOnViewportResize = (props == m_canvasViewportMatrixProps);
- }
- void ViewportInteraction::ProcessInteraction(const AZ::Vector2& mousePosition,
- Qt::KeyboardModifiers modifiers,
- const QTreeWidgetItemRawPtrQList& selectedItems)
- {
- // Get the mouse move delta, which is in viewport space.
- AZ::Vector2 delta = mousePosition - m_lastMouseDragPos;
- AZ::Vector3 mouseTranslation(delta.GetX(), delta.GetY(), 0.0f);
- BeginReversibleAction(selectedItems);
- bool ctrlIsPressed = modifiers.testFlag(Qt::ControlModifier);
- if (m_interactionType == InteractionType::NONE)
- {
- if (m_isAreaSelectionActive)
- {
- float mouseDragDistance2 = (mousePosition - m_startMouseDragPos).GetLengthSq();
- if (mouseDragDistance2 >= g_minAreaSelectionDistance2)
- {
- // Area selection
- AZ::Vector2 rectMin(min(m_startMouseDragPos.GetX(), mousePosition.GetX()), min(m_startMouseDragPos.GetY(), mousePosition.GetY()));
- AZ::Vector2 rectMax(max(m_startMouseDragPos.GetX(), mousePosition.GetX()), max(m_startMouseDragPos.GetY(), mousePosition.GetY()));
- LyShine::EntityArray elementsToSelect;
- UiCanvasBus::EventResult(
- elementsToSelect, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::PickElements, rectMin, rectMax);
- if (ctrlIsPressed)
- {
- // NOTE: We are fighting against SetSelectedItems a bit here. SetSelectedItems uses Qt
- // to set the selection and the control and shift modifiers affect its behavior.
- // When Ctrl is down, unless you pass null or an empty list it adds to the existing
- // selected items. To get the behavior we want when ctrl is held down we have to clear
- // the selection before setting it. NOTE: if you area select over a group and (during
- // same drag) move the cursor so that they are not in the box then they should not
- // be added to the selection.
- HierarchyHelpers::SetSelectedItem(m_editorWindow->GetHierarchy(), nullptr);
- // when control is pressed we add the selected elements in a drag select to the already selected elements
- // NOTE: It would be nice to allow ctrl-area-select to deselect already selected items. However, the main
- // level editor does not behave that way and we are trying to be consistent (see LMBR-10377)
- for (auto element : m_selectedElementsAtSelectionStart)
- {
- // if not already in the selectedElements then add it
- auto iter = AZStd::find(elementsToSelect.begin(), elementsToSelect.end(), element);
- if (iter == elementsToSelect.end())
- {
- elementsToSelect.push_back(element);
- }
- }
- }
- HierarchyHelpers::SetSelectedItems(m_editorWindow->GetHierarchy(), &elementsToSelect);
- }
- else
- {
- // Selection area too small, ignore
- }
- }
- }
- else if (m_interactionType == InteractionType::PIVOT)
- {
- // Move the pivot that was grabbed
- ViewportElement::MovePivot(m_lastMouseDragPos, EntityHelpers::GetEntity(m_activeElementId), mousePosition);
- }
- else if (m_interactionType == InteractionType::ANCHORS)
- {
- // Move the anchors of the active element
- ViewportElement::MoveAnchors(m_grabbedAnchors, m_startAnchors, m_startMouseDragPos, EntityHelpers::GetEntity(m_activeElementId), mousePosition, ctrlIsPressed);
- }
- else if (m_interactionType == InteractionType::TRANSFORM_GIZMO)
- {
- // Transform all selected elements by interacting with one element's transform gizmo
- switch (m_interactionMode)
- {
- case InteractionMode::MOVE:
- case InteractionMode::ANCHOR:
- if (m_dragInteraction)
- {
- m_dragInteraction->Update(mousePosition);
- }
- break;
- case InteractionMode::ROTATE:
- {
- LyShine::EntityArray selectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
- for (auto element : selectedElements)
- {
- ViewportElement::Rotate(m_editorWindow->GetHierarchy(), m_editorWindow->GetCanvas(), m_lastMouseDragPos, m_activeElementId, element, mousePosition);
- }
- }
- break;
- case InteractionMode::RESIZE:
- {
- if (!ViewportHelpers::IsControlledByLayout(EntityHelpers::GetEntity(m_activeElementId)))
- {
- LyShine::EntityArray selectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
- for (auto element : selectedElements)
- {
- ViewportElement::ResizeByGizmo(m_editorWindow->GetHierarchy(), m_editorWindow->GetCanvas(), m_grabbedGizmoParts, m_activeElementId, element, mouseTranslation);
- }
- }
- }
- break;
- default:
- AZ_Assert(0, "Unexpected combination of m_interactionMode and m_interactionType.");
- break;
- }
- }
- else if (m_interactionType == InteractionType::DIRECT)
- {
- // Transform all selected elements by interacting with one element directly
- switch (m_interactionMode)
- {
- case InteractionMode::MOVE:
- case InteractionMode::ANCHOR:
- if (m_dragInteraction)
- {
- m_dragInteraction->Update(mousePosition);
- }
- break;
- case InteractionMode::RESIZE:
- // Exception: Direct resizing (grabbing an edge) only affects the element you grabbed
- ViewportElement::ResizeDirectly(m_editorWindow->GetHierarchy(), m_editorWindow->GetCanvas(), m_grabbedEdges, EntityHelpers::GetEntity(m_activeElementId), mouseTranslation);
- break;
- default:
- AZ_Assert(0, "Unexpected combination of m_interactionMode and m_interactionType.");
- break;
- }
- }
- else if (m_interactionType == InteractionType::GUIDE)
- {
- if (m_dragInteraction)
- {
- m_dragInteraction->Update(mousePosition);
- }
- }
- else
- {
- AZ_Assert(0, "Unexpected value for m_interactionType.");
- }
- // Tell the Properties panel to update
- const AZ::Uuid& transformComponentType = InitAndGetTransformComponentType();
- m_editorWindow->GetProperties()->TriggerRefresh(AzToolsFramework::PropertyModificationRefreshLevel::Refresh_Values, &transformComponentType);
- }
- void ViewportInteraction::DrawAxisGizmo(Draw2dHelper& draw2d, const AZ::Entity* element, CoordinateSystem coordinateSystem, const ViewportIcon* lineTextureX, const ViewportIcon* lineTextureY)
- {
- if (UiTransformBus::FindFirstHandler(element->GetId()))
- {
- AZ::Vector2 pivotPosition;
- AZ::Matrix4x4 transform;
- bool isMoveOrAnchorMode = m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR;
- if (coordinateSystem == CoordinateSystem::LOCAL)
- {
- UiTransformBus::EventResult(pivotPosition, element->GetId(), &UiTransformBus::Events::GetCanvasSpacePivotNoScaleRotate);
- // LOCAL MOVE in the parent element's LOCAL space.
- AZ::EntityId elementId(isMoveOrAnchorMode ? EntityHelpers::GetParentElement(element)->GetId() : element->GetId());
- UiTransformBus::Event(elementId, &UiTransformBus::Events::GetTransformToViewport, transform);
- }
- else
- {
- // View coordinate system: do everything in viewport space
- UiTransformBus::EventResult(pivotPosition, element->GetId(), &UiTransformBus::Events::GetViewportSpacePivot);
- transform = AZ::Matrix4x4::CreateIdentity();
- }
- // Draw up axis
- if (isMoveOrAnchorMode || !ViewportHelpers::IsVerticallyFit(element))
- {
- AZ::Color color = ((m_activeElementId == element->GetId()) && m_grabbedGizmoParts.m_top) ? ViewportHelpers::highlightColor : ViewportHelpers::yColor;
- lineTextureY->Draw(draw2d, pivotPosition, transform, 0.0f, color);
- }
- // Draw right axis
- if (isMoveOrAnchorMode || !ViewportHelpers::IsHorizontallyFit(element))
- {
- AZ::Color color = ((m_activeElementId == element->GetId()) && m_grabbedGizmoParts.m_right) ? ViewportHelpers::highlightColor : ViewportHelpers::xColor;
- lineTextureX->Draw(draw2d, pivotPosition, transform, 0.0f, color);
- }
- // Draw center square
- if (isMoveOrAnchorMode || !ViewportHelpers::IsHorizontallyFit(element) && !ViewportHelpers::IsVerticallyFit(element))
- {
- AZ::Color color = ((m_activeElementId == element->GetId()) && m_grabbedGizmoParts.Both()) ? ViewportHelpers::highlightColor : ViewportHelpers::zColor;
- m_centerSquare->Draw(draw2d, pivotPosition, transform, 0.0f, color);
- }
- }
- }
- void ViewportInteraction::DrawCircleGizmo(Draw2dHelper& draw2d, const AZ::Entity* element)
- {
- if (UiTransformBus::FindFirstHandler(element->GetId()))
- {
- AZ::Vector2 pivotPosition;
- UiTransformBus::EventResult(pivotPosition, element->GetId(), &UiTransformBus::Events::GetViewportSpacePivot);
- // Draw circle
- AZ::Color color = ((m_activeElementId == element->GetId()) && m_interactionType == InteractionType::TRANSFORM_GIZMO) ? ViewportHelpers::highlightColor : ViewportHelpers::zColor;
- m_circle->Draw(draw2d, pivotPosition, AZ::Matrix4x4::CreateIdentity(), 0.0f, color);
- }
- }
- void ViewportInteraction::UpdateCoordinateSystemToolbarSection()
- {
- // the coordinate system toolbar should only be enabled in move or anchor mode
- bool isMoveOrAnchorMode = m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR;
- m_editorWindow->GetCoordinateSystemToolbarSection()->SetIsEnabled(isMoveOrAnchorMode);
- }
- #include <moc_ViewportInteraction.cpp>
|