ViewportInteraction.cpp 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "EditorCommon.h"
  9. #include "ViewportNudge.h"
  10. #include "ViewportElement.h"
  11. #include "QtHelpers.h"
  12. #include "GuideHelpers.h"
  13. #include "AlignToolbarSection.h"
  14. #include "ViewportMoveInteraction.h"
  15. #include "ViewportMoveGuideInteraction.h"
  16. #include <LyShine/UiComponentTypes.h>
  17. #include <LyShine/Bus/UiEditorCanvasBus.h>
  18. #include <AzCore/Casting/numeric_cast.h>
  19. #include <AzCore/Math/Vector2.h>
  20. #include <AzQtComponents/Components/Widgets/ToolBar.h>
  21. #include <QKeyEvent>
  22. #include <QMouseEvent>
  23. #include <QToolButton>
  24. #include <QTextDocumentFragment>
  25. #include <QSettings>
  26. #include <Editor/Resource.h>
  27. #include <Editor/Util/EditorUtils.h>
  28. static const float g_elementEdgeForgiveness = 10.0f;
  29. // The square of the minimum corner-to-corner distance for an area selection
  30. static const float g_minAreaSelectionDistance2 = 100.0f;
  31. #define UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_INTERACTION_MODE_KEY "ViewportWidget::m_interactionMode"
  32. #define UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_INTERACTION_MODE_DEFAULT ( ViewportInteraction::InteractionMode::SELECTION )
  33. #define UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_COORDINATE_SYSTEM_KEY "ViewportWidget::m_coordinateSystem"
  34. #define UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_COORDINATE_SYSTEM_DEFAULT ( ViewportInteraction::CoordinateSystem::LOCAL )
  35. namespace
  36. {
  37. const float defaultCanvasToViewportScaleIncrement = 0.20f;
  38. ViewportInteraction::InteractionMode PersistentGetInteractionMode()
  39. {
  40. QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
  41. settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
  42. int defaultMode = static_cast<int>(UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_INTERACTION_MODE_DEFAULT);
  43. int result = settings.value(
  44. UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_INTERACTION_MODE_KEY,
  45. defaultMode).toInt();
  46. settings.endGroup();
  47. return static_cast<ViewportInteraction::InteractionMode>(result);
  48. }
  49. void PersistentSetInteractionMode(ViewportInteraction::InteractionMode mode)
  50. {
  51. QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
  52. settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
  53. settings.setValue(UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_INTERACTION_MODE_KEY,
  54. static_cast<int>(mode));
  55. settings.endGroup();
  56. }
  57. ViewportInteraction::CoordinateSystem PersistentGetCoordinateSystem()
  58. {
  59. QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
  60. settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
  61. int defaultSystem = static_cast<int>(UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_COORDINATE_SYSTEM_DEFAULT);
  62. int result = settings.value(
  63. UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_COORDINATE_SYSTEM_KEY,
  64. defaultSystem).toInt();
  65. settings.endGroup();
  66. return static_cast<ViewportInteraction::CoordinateSystem>(result);
  67. }
  68. void PersistentSetCoordinateSystem(ViewportInteraction::CoordinateSystem coordinateSystem)
  69. {
  70. QSettings settings(QSettings::IniFormat, QSettings::UserScope, AZ_QCOREAPPLICATION_SETTINGS_ORGANIZATION_NAME);
  71. settings.beginGroup(UICANVASEDITOR_NAME_SHORT);
  72. settings.setValue(UICANVASEDITOR_SETTINGS_VIEWPORTINTERACTION_COORDINATE_SYSTEM_KEY,
  73. static_cast<int>(coordinateSystem));
  74. settings.endGroup();
  75. }
  76. } // anonymous namespace.
  77. class ViewportInteractionExpanderWatcher
  78. : public QObject
  79. {
  80. public:
  81. ViewportInteractionExpanderWatcher(QObject* parent = nullptr)
  82. : QObject(parent)
  83. {
  84. }
  85. bool eventFilter(QObject* obj, QEvent* event) override
  86. {
  87. switch (event->type())
  88. {
  89. case QEvent::MouseButtonPress:
  90. case QEvent::MouseButtonRelease:
  91. case QEvent::MouseButtonDblClick:
  92. {
  93. if (qobject_cast<QToolButton*>(obj))
  94. {
  95. auto mouseEvent = static_cast<QMouseEvent*>(event);
  96. auto expansion = qobject_cast<QToolButton*>(obj);
  97. expansion->setPopupMode(QToolButton::InstantPopup);
  98. auto menu = new QMenu(expansion);
  99. auto toolbar = qobject_cast<QToolBar*>(expansion->parentWidget());
  100. for (auto toolbarAction : toolbar->actions())
  101. {
  102. auto actionWidget = toolbar->widgetForAction(toolbarAction);
  103. if (actionWidget && !actionWidget->isVisible() && !toolbarAction->text().isEmpty())
  104. {
  105. QString plainText = QTextDocumentFragment::fromHtml(actionWidget->toolTip()).toPlainText();
  106. toolbarAction->setText(plainText);
  107. menu->addAction(toolbarAction);
  108. }
  109. }
  110. if (menu->actions().count() == 0)
  111. {
  112. QAction* noAction = new QAction(this);
  113. noAction->setEnabled(false);
  114. noAction->setText(tr("Please resize the toolbar to see all the controls."));
  115. menu->addAction(noAction);
  116. }
  117. menu->exec(mouseEvent->globalPos());
  118. return true;
  119. }
  120. break;
  121. }
  122. }
  123. return QObject::eventFilter(obj, event);
  124. }
  125. };
  126. ViewportInteraction::ViewportInteraction(EditorWindow* editorWindow)
  127. : QObject()
  128. , m_editorWindow(editorWindow)
  129. , m_activeElementId()
  130. , m_anchorWhole(new ViewportIcon("Editor/Icons/Viewport/Anchor_Whole.tif"))
  131. , m_pivotIcon(new ViewportIcon("Editor/Icons/Viewport/Pivot.tif"))
  132. , m_interactionMode(PersistentGetInteractionMode())
  133. , m_interactionType(InteractionType::NONE)
  134. , m_coordinateSystem(PersistentGetCoordinateSystem())
  135. , m_spaceBarIsActive(false)
  136. , m_leftButtonIsActive(false)
  137. , m_middleButtonIsActive(false)
  138. , m_reversibleActionStarted(false)
  139. , m_startMouseDragPos(0.0f, 0.0f)
  140. , m_lastMouseDragPos(0.0f, 0.0f)
  141. , m_selectedElementsAtSelectionStart()
  142. , m_canvasViewportMatrixProps()
  143. , m_shouldScaleToFitOnViewportResize(true)
  144. , m_transformComponentType(AZ::Uuid::CreateNull())
  145. , m_grabbedEdges(ViewportHelpers::ElementEdges())
  146. , m_startAnchors(UiTransform2dInterface::Anchors())
  147. , m_grabbedAnchors(ViewportHelpers::SelectedAnchors())
  148. , m_grabbedGizmoParts(ViewportHelpers::GizmoParts())
  149. , m_lineTriangleX(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Line_Triangle_X.tif"))
  150. , m_lineTriangleY(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Line_Triangle_Y.tif"))
  151. , m_circle(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Circle.tif"))
  152. , m_lineSquareX(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Line_Square_X.tif"))
  153. , m_lineSquareY(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Line_Square_Y.tif"))
  154. , m_centerSquare(new ViewportIcon("Editor/Icons/Viewport/Transform_Gizmo_Center_Square.tif"))
  155. , m_dottedLine(new ViewportIcon("Editor/Icons/Viewport/DottedLine.tif"))
  156. , m_dragInteraction(nullptr)
  157. , m_expanderWatcher(new ViewportInteractionExpanderWatcher(this))
  158. {
  159. m_cursorRotate = CMFCUtils::LoadCursor(IDC_POINTER_OBJECT_ROTATE);
  160. }
  161. ViewportInteraction::~ViewportInteraction()
  162. {
  163. }
  164. void ViewportInteraction::ClearInteraction(bool clearSpaceBarIsActive)
  165. {
  166. m_activeElementId.SetInvalid();
  167. m_interactionType = InteractionType::NONE;
  168. m_spaceBarIsActive = clearSpaceBarIsActive ? false : m_spaceBarIsActive;
  169. m_leftButtonIsActive = false;
  170. m_middleButtonIsActive = false;
  171. m_startMouseDragPos = AZ::Vector2::CreateZero();
  172. m_lastMouseDragPos = AZ::Vector2::CreateZero();
  173. m_grabbedEdges = ViewportHelpers::ElementEdges();
  174. m_startAnchors = UiTransform2dInterface::Anchors();
  175. m_grabbedAnchors = ViewportHelpers::SelectedAnchors();
  176. m_grabbedGizmoParts = ViewportHelpers::GizmoParts();
  177. m_selectedElementsAtSelectionStart.clear();
  178. m_isAreaSelectionActive = false;
  179. m_reversibleActionStarted = false;
  180. SAFE_DELETE(m_dragInteraction);
  181. }
  182. void ViewportInteraction::Nudge(NudgeDirection direction, NudgeSpeed speed)
  183. {
  184. const AZ::Uuid& transformComponentType = InitAndGetTransformComponentType();
  185. ViewportNudge::Nudge(m_editorWindow,
  186. m_interactionMode,
  187. m_editorWindow->GetViewport(),
  188. direction,
  189. speed,
  190. m_editorWindow->GetHierarchy()->selectedItems(),
  191. m_coordinateSystem,
  192. transformComponentType);
  193. }
  194. void ViewportInteraction::StartObjectPickMode()
  195. {
  196. // Temporarily set the viewport interaction mode to "Selection" and disable the toolbar
  197. m_interactionModeBeforePickMode = GetMode();
  198. SetMode(InteractionMode::SELECTION);
  199. m_editorWindow->GetModeToolbar()->setEnabled(false);
  200. InvalidateHoverElement();
  201. UpdateCursor();
  202. }
  203. void ViewportInteraction::StopObjectPickMode()
  204. {
  205. bool mousePressed = GetLeftButtonIsActive();
  206. m_editorWindow->GetModeToolbar()->setEnabled(true);
  207. SetMode(m_interactionModeBeforePickMode);
  208. SetCursorStr("");
  209. m_hoverElement.SetInvalid();
  210. // Update interaction type and cursor right away if the mouse is already released (user pressed ESC to cancel pick mode)
  211. // instead of waiting for a mouse move/release event
  212. if (!mousePressed)
  213. {
  214. QPoint viewportCursorPos = m_editorWindow->GetViewport()->mapFromGlobal(QCursor::pos());
  215. QTreeWidgetItemRawPtrQList selectedItems = m_editorWindow->GetHierarchy()->selectedItems();
  216. UpdateInteractionType(AZ::Vector2(aznumeric_cast<float>(viewportCursorPos.x()), aznumeric_cast<float>(viewportCursorPos.y())), selectedItems);
  217. }
  218. UpdateCursor();
  219. }
  220. bool ViewportInteraction::GetLeftButtonIsActive()
  221. {
  222. return m_leftButtonIsActive;
  223. }
  224. bool ViewportInteraction::GetSpaceBarIsActive()
  225. {
  226. return m_spaceBarIsActive;
  227. }
  228. void ViewportInteraction::ActivateSpaceBar()
  229. {
  230. m_spaceBarIsActive = true;
  231. UpdateCursor();
  232. if (m_editorWindow->GetViewport()->IsInObjectPickMode())
  233. {
  234. // Don't highlight the hover element during a pan
  235. InvalidateHoverElement();
  236. }
  237. }
  238. void ViewportInteraction::Draw(Draw2dHelper& draw2d,
  239. const QTreeWidgetItemRawPtrQList& selectedItems)
  240. {
  241. // Draw border around hover UI element
  242. if (m_hoverElement.IsValid())
  243. {
  244. m_editorWindow->GetViewport()->GetViewportHighlight()->DrawHover(draw2d, m_hoverElement);
  245. }
  246. // Draw the guide lines
  247. if (m_editorWindow->GetViewport()->AreGuidesShown())
  248. {
  249. GuideHelpers::DrawGuideLines(m_editorWindow->GetCanvas(), m_editorWindow->GetViewport(), draw2d);
  250. }
  251. // Draw the transform gizmo where appropriate
  252. if (m_interactionMode != InteractionMode::SELECTION)
  253. {
  254. LyShine::EntityArray selectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
  255. switch (m_interactionMode)
  256. {
  257. case InteractionMode::MOVE:
  258. case InteractionMode::ANCHOR:
  259. for (auto element : selectedElements)
  260. {
  261. if (!ViewportHelpers::IsControlledByLayout(element))
  262. {
  263. DrawAxisGizmo(draw2d, element, m_coordinateSystem, m_lineTriangleX.get(), m_lineTriangleY.get());
  264. }
  265. }
  266. break;
  267. case InteractionMode::ROTATE:
  268. for (auto element : selectedElements)
  269. {
  270. DrawCircleGizmo(draw2d, element);
  271. }
  272. break;
  273. case InteractionMode::RESIZE:
  274. for (auto element : selectedElements)
  275. {
  276. if (!ViewportHelpers::IsControlledByLayout(element))
  277. {
  278. DrawAxisGizmo(draw2d, element, CoordinateSystem::LOCAL, m_lineSquareX.get(), m_lineSquareY.get());
  279. }
  280. }
  281. break;
  282. }
  283. }
  284. // Draw the area selection, if there is one
  285. if (AreaSelectionIsActive())
  286. {
  287. m_dottedLine->DrawAxisAlignedBoundingBox(draw2d, m_startMouseDragPos, m_lastMouseDragPos);
  288. }
  289. // If there is an active drag interaction give it a chance to render its interaction display
  290. if (m_dragInteraction)
  291. {
  292. m_dragInteraction->Render(draw2d);
  293. }
  294. // Draw the cursor string
  295. if (!m_cursorStr.empty() && m_editorWindow->GetViewport()->underMouse())
  296. {
  297. ViewportHelpers::DrawCursorText(m_cursorStr, draw2d, m_editorWindow->GetViewport());
  298. }
  299. }
  300. bool ViewportInteraction::AreaSelectionIsActive()
  301. {
  302. return m_isAreaSelectionActive;
  303. }
  304. void ViewportInteraction::BeginReversibleAction(const QTreeWidgetItemRawPtrQList& selectedItems)
  305. {
  306. if ((m_reversibleActionStarted) ||
  307. (m_interactionType == InteractionType::NONE || m_interactionType == InteractionType::GUIDE) ||
  308. (m_interactionMode == InteractionMode::SELECTION))
  309. {
  310. // Nothing to do.
  311. return;
  312. }
  313. // we are about to change something and we have not started an undo action yet, start one
  314. m_reversibleActionStarted = true;
  315. // Tell the Properties panel that we're about to do a reversible action
  316. HierarchyClipboard::BeginUndoableEntitiesChange(m_editorWindow, m_selectedEntitiesUndoState);
  317. // Snapping.
  318. bool isSnapping = false;
  319. UiEditorCanvasBus::EventResult(isSnapping, m_editorWindow->GetCanvas(), &UiEditorCanvasBus::Events::GetIsSnapEnabled);
  320. if (isSnapping)
  321. {
  322. // Set all initial non-snapped values.
  323. HierarchyItemRawPtrList items = SelectionHelpers::GetSelectedHierarchyItems(m_editorWindow->GetHierarchy(),
  324. selectedItems);
  325. for (auto i : items)
  326. {
  327. UiTransform2dInterface::Offsets offsets;
  328. UiTransform2dBus::EventResult(offsets, i->GetEntityId(), &UiTransform2dBus::Events::GetOffsets);
  329. i->SetNonSnappedOffsets(offsets);
  330. float rotation;
  331. UiTransformBus::EventResult(rotation, i->GetEntityId(), &UiTransformBus::Events::GetZRotation);
  332. i->SetNonSnappedZRotation(rotation);
  333. }
  334. }
  335. }
  336. void ViewportInteraction::EndReversibleAction()
  337. {
  338. if (!m_reversibleActionStarted)
  339. {
  340. // Nothing to do.
  341. return;
  342. }
  343. m_reversibleActionStarted = false;
  344. if (AreaSelectionIsActive())
  345. {
  346. // Nothing to do.
  347. return;
  348. }
  349. // Note that EndReversibleAction is not used for interactions that handle undo in a m_dragInteraction
  350. // Ideally we will change them all to use m_dragInteraction and handle the undo there.
  351. HierarchyClipboard::EndUndoableEntitiesChange(m_editorWindow, "viewport interaction", m_selectedEntitiesUndoState);
  352. }
  353. void ViewportInteraction::MousePressEvent(QMouseEvent* ev)
  354. {
  355. AZ::Vector2 mousePosition = QtHelpers::QPointFToVector2(ev->localPos());
  356. m_startMouseDragPos = m_lastMouseDragPos = mousePosition;
  357. const bool ctrlKeyPressed = ev->modifiers().testFlag(Qt::ControlModifier);
  358. // Detect whether an entity was picked on the mouse press so that
  359. // mouse move/release events can be handled appropriately
  360. m_entityPickedOnMousePress = false;
  361. // Prepare to handle panning
  362. if ((!m_leftButtonIsActive) &&
  363. (ev->button() == Qt::MiddleButton))
  364. {
  365. m_middleButtonIsActive = true;
  366. }
  367. else if ((!m_middleButtonIsActive) &&
  368. (ev->button() == Qt::LeftButton))
  369. {
  370. // Prepare for clicking and dragging
  371. m_leftButtonIsActive = true;
  372. if (m_activeElementId.IsValid())
  373. {
  374. if (m_grabbedAnchors.Any())
  375. {
  376. // Prepare to move anchors
  377. UiTransform2dBus::EventResult(m_startAnchors, m_activeElementId, &UiTransform2dBus::Events::GetAnchors);
  378. }
  379. else if (m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR)
  380. {
  381. // Prepare for moving elements by offsets or anchors
  382. QTreeWidgetItemRawPtrQList selectedItems = m_editorWindow->GetHierarchy()->selectedItems();
  383. m_dragInteraction = new ViewportMoveInteraction(m_editorWindow->GetHierarchy(), selectedItems,
  384. m_editorWindow->GetCanvas(), GetActiveElement(), m_coordinateSystem, m_grabbedGizmoParts, m_interactionMode, m_interactionType, mousePosition);
  385. }
  386. }
  387. else if (m_interactionType == InteractionType::GUIDE)
  388. {
  389. // We are hovering over a guide with the move guide icon displayed so start the move guide interaction
  390. m_dragInteraction = new ViewportMoveGuideInteraction(m_editorWindow, m_editorWindow->GetCanvas(),
  391. m_activeGuideIsVertical, m_activeGuideIndex, mousePosition);
  392. }
  393. }
  394. // If there isn't another interaction happening, try to select an element
  395. if ((!m_spaceBarIsActive && !m_middleButtonIsActive && m_interactionType == InteractionType::NONE) ||
  396. ((m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR) && m_interactionType == InteractionType::DIRECT && ctrlKeyPressed))
  397. {
  398. if (m_editorWindow->GetViewport()->IsInObjectPickMode())
  399. {
  400. AZ::Entity* element = nullptr;
  401. UiCanvasBus::EventResult(element, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::PickElement, mousePosition);
  402. m_editorWindow->GetViewport()->PickItem(element ? element->GetId() : AZ::EntityId());
  403. m_entityPickedOnMousePress = true;
  404. }
  405. else
  406. {
  407. QTreeWidgetItemRawPtrQList selectedItems = m_editorWindow->GetHierarchy()->selectedItems();
  408. // 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
  409. // click on them, thinking it might do something, and it changes the selection.
  410. // 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.
  411. // 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.
  412. bool ignoreClickForSelection = false;
  413. if ((m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR) && m_interactionType == InteractionType::NONE)
  414. {
  415. LyShine::EntityArray topLevelSelectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
  416. for (auto elementWithAnchors : topLevelSelectedElements)
  417. {
  418. ViewportHelpers::SelectedAnchors grabbedAnchors;
  419. if (!ViewportHelpers::IsControlledByLayout(elementWithAnchors) &&
  420. ViewportElement::PickAnchors(elementWithAnchors, mousePosition, m_anchorWhole->GetTextureSize(), grabbedAnchors))
  421. {
  422. // Hovering over anchors, if the click is outside the element with the anchors then ignore
  423. bool isElementUnderCursor = false;
  424. UiTransformBus::EventResult(
  425. isElementUnderCursor, elementWithAnchors->GetId(), &UiTransformBus::Events::IsPointInRect, mousePosition);
  426. if (!isElementUnderCursor)
  427. {
  428. ignoreClickForSelection = true;
  429. break;
  430. }
  431. }
  432. }
  433. }
  434. if (!ignoreClickForSelection)
  435. {
  436. AZ::Entity* element = nullptr;
  437. UiCanvasBus::EventResult(element, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::PickElement, mousePosition);
  438. HierarchyWidget* hierarchyWidget = m_editorWindow->GetHierarchy();
  439. QTreeWidgetItem* widgetItem = nullptr;
  440. bool itemDeselected = false;
  441. // store the selected items at the start of the selection
  442. m_selectedElementsAtSelectionStart = SelectionHelpers::GetSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
  443. if (element)
  444. {
  445. widgetItem = HierarchyHelpers::ElementToItem(hierarchyWidget, element, false);
  446. // If user is selecting something with the control key pressed, the
  447. // element may need to be de-selected (if it's already selected).
  448. itemDeselected = HierarchyHelpers::HandleDeselect(widgetItem, ctrlKeyPressed);
  449. }
  450. // If the item didn't need to be de-selected, then we should select it
  451. if (!itemDeselected)
  452. {
  453. // Note that widgetItem could still be null at this point, but
  454. // SetSelectedItem will handle this situation for us.
  455. HierarchyHelpers::SetSelectedItem(hierarchyWidget, element);
  456. }
  457. // ClearInteraction gets called if the selection changes to empty but we do not want to clear these since we can start a drag now
  458. m_leftButtonIsActive = true;
  459. m_startMouseDragPos = m_lastMouseDragPos = mousePosition;
  460. m_isAreaSelectionActive = true;
  461. }
  462. }
  463. }
  464. UpdateCursor();
  465. }
  466. void ViewportInteraction::PanOnMouseMoveEvent(const AZ::Vector2& mousePosition)
  467. {
  468. AZ::Vector2 deltaPosition = mousePosition - m_lastMouseDragPos;
  469. AZ::Vector3 mousePosDelta(EntityHelpers::MakeVec3(deltaPosition));
  470. m_canvasViewportMatrixProps.translation += mousePosDelta;
  471. UpdateCanvasToViewportMatrix();
  472. UpdateShouldScaleToFitOnResize();
  473. }
  474. const AZ::Uuid& ViewportInteraction::InitAndGetTransformComponentType()
  475. {
  476. if (m_transformComponentType.IsNull())
  477. {
  478. m_transformComponentType = LyShine::UiTransform2dComponentUuid;
  479. }
  480. return m_transformComponentType;
  481. }
  482. void ViewportInteraction::MouseMoveEvent(QMouseEvent* ev,
  483. const QTreeWidgetItemRawPtrQList& selectedItems)
  484. {
  485. AZ::Vector2 mousePosition = QtHelpers::QPointFToVector2(ev->localPos());
  486. if (m_spaceBarIsActive)
  487. {
  488. if (m_leftButtonIsActive || m_middleButtonIsActive)
  489. {
  490. PanOnMouseMoveEvent(mousePosition);
  491. }
  492. }
  493. else if (m_leftButtonIsActive)
  494. {
  495. if (!m_entityPickedOnMousePress)
  496. {
  497. // Click and drag
  498. ProcessInteraction(mousePosition,
  499. ev->modifiers(),
  500. selectedItems);
  501. }
  502. }
  503. else if (m_middleButtonIsActive)
  504. {
  505. PanOnMouseMoveEvent(mousePosition);
  506. }
  507. else if (ev->buttons() == Qt::NoButton)
  508. {
  509. // Hover
  510. if (m_editorWindow->GetViewport()->IsInObjectPickMode())
  511. {
  512. // Update hover element. We only display the hover element in object pick mode
  513. UpdateHoverElement(mousePosition);
  514. }
  515. else
  516. {
  517. m_interactionType = InteractionType::NONE;
  518. m_grabbedEdges.SetAll(false);
  519. m_grabbedAnchors.SetAll(false);
  520. m_grabbedGizmoParts.SetBoth(false);
  521. UpdateInteractionType(mousePosition,
  522. selectedItems);
  523. UpdateCursor();
  524. }
  525. }
  526. m_lastMouseDragPos = mousePosition;
  527. }
  528. void ViewportInteraction::MouseReleaseEvent(QMouseEvent* ev,
  529. [[maybe_unused]] const QTreeWidgetItemRawPtrQList& selectedItems)
  530. {
  531. if (!m_entityPickedOnMousePress)
  532. {
  533. // if the mouse press and release were in the same position and
  534. // no changes have been made then we can treat it as a mouse-click which can
  535. // do selection. This is useful in the case where we are in move mode but just
  536. // clicked on something that is either:
  537. // - one of multiple things selected and we want to just select this
  538. // - an element in front of something that is selected
  539. // In this case the mouse press will not have been treated as selection in
  540. // MousePressEvent so we need to handle this as a special case
  541. if (!m_reversibleActionStarted && m_lastMouseDragPos == m_startMouseDragPos &&
  542. ev->button() == Qt::LeftButton && !ev->modifiers().testFlag(Qt::ControlModifier) &&
  543. (m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR)
  544. && (m_interactionType == InteractionType::DIRECT || m_interactionType == InteractionType::TRANSFORM_GIZMO))
  545. {
  546. AZ::Vector2 mousePosition = QtHelpers::QPointFToVector2(ev->localPos());
  547. bool ignoreClick = false;
  548. if (m_interactionType == InteractionType::TRANSFORM_GIZMO)
  549. {
  550. // if we clicked on a gizmo but didn't move then we want to consider this a select click as long as the click
  551. // was inside the active element. (the square part of the gizmo can cover a large area of the element so ignoring
  552. // the click is confusing).
  553. if (m_activeElementId.IsValid())
  554. {
  555. bool isActiveElementUnderCursor = false;
  556. UiTransformBus::EventResult(
  557. isActiveElementUnderCursor, m_activeElementId, &UiTransformBus::Events::IsPointInRect, mousePosition);
  558. if (!isActiveElementUnderCursor)
  559. {
  560. ignoreClick = true;
  561. }
  562. }
  563. }
  564. if (!ignoreClick)
  565. {
  566. AZ::Entity* element = nullptr;
  567. UiCanvasBus::EventResult(element, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::PickElement, mousePosition);
  568. HierarchyWidget* hierarchyWidget = m_editorWindow->GetHierarchy();
  569. if (element)
  570. {
  571. HierarchyHelpers::SetSelectedItem(hierarchyWidget, element);
  572. }
  573. }
  574. }
  575. if (m_dragInteraction)
  576. {
  577. // test to see if the mouse position is inside the viewport on each axis
  578. const QPoint& pos = ev->pos();
  579. const AZ::Vector2 size = m_editorWindow->GetViewport()->GetRenderViewportSize();
  580. ViewportDragInteraction::EndState inWidget;
  581. if (pos.x() >= 0 && pos.x() < size.GetX())
  582. inWidget = pos.y() >= 0 && pos.y() < size.GetY() ? ViewportDragInteraction::EndState::Inside : ViewportDragInteraction::EndState::OutsideY;
  583. else
  584. inWidget = pos.y() >= 0 && pos.y() < size.GetY() ? ViewportDragInteraction::EndState::OutsideX : ViewportDragInteraction::EndState::OutsideXY;
  585. // Some interactions end differently depending on whether the mouse was released inside or outside the viewport
  586. m_dragInteraction->EndInteraction(inWidget);
  587. }
  588. // Tell the Properties panel to update.
  589. // Refresh attributes as well in case this change affects an attribute (ex. anchors affect warning text on scale to device mode)
  590. const AZ::Uuid& transformComponentType = InitAndGetTransformComponentType();
  591. m_editorWindow->GetProperties()->TriggerRefresh(AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues, &transformComponentType);
  592. // Tell the Properties panel that the reversible action is complete
  593. EndReversibleAction();
  594. }
  595. // Reset the interaction
  596. ClearInteraction(false);
  597. if (!m_spaceBarIsActive)
  598. {
  599. // Immediately update the interaction type and cursor (using the possibly new selection)
  600. AZ::Vector2 mousePosition = QtHelpers::QPointFToVector2(ev->localPos());
  601. UpdateInteractionType(mousePosition,
  602. m_editorWindow->GetHierarchy()->selectedItems());
  603. }
  604. UpdateCursor();
  605. }
  606. bool ViewportInteraction::MouseWheelEvent(QWheelEvent* ev)
  607. {
  608. if (m_leftButtonIsActive || m_middleButtonIsActive)
  609. {
  610. // Ignore event.
  611. return false;
  612. }
  613. const QPoint numDegrees(ev->angleDelta());
  614. if (!numDegrees.isNull())
  615. {
  616. // Angle delta returns distance rotated by mouse wheel in eigths of a
  617. // degree.
  618. static const int numStepsPerDegree = 8;
  619. const float numScrollDegrees = aznumeric_cast<float>(numDegrees.y() / numStepsPerDegree);
  620. static const float zoomMultiplier = 1 / 100.0f;
  621. float newScale = m_canvasViewportMatrixProps.scale + numScrollDegrees * zoomMultiplier;
  622. SetCanvasToViewportScale(QuantizeZoomScale(newScale), AZ::Vector2(ev->position().x(), ev->position().y()));
  623. }
  624. return true;
  625. }
  626. bool ViewportInteraction::KeyPressEvent(QKeyEvent* ev)
  627. {
  628. switch (ev->key())
  629. {
  630. case Qt::Key_Space:
  631. if (!ev->isAutoRepeat())
  632. {
  633. ActivateSpaceBar();
  634. }
  635. return true;
  636. case Qt::Key_Up:
  637. Nudge(ViewportInteraction::NudgeDirection::Up,
  638. (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
  639. return true;
  640. case Qt::Key_Down:
  641. Nudge(ViewportInteraction::NudgeDirection::Down,
  642. (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
  643. return true;
  644. case Qt::Key_Left:
  645. Nudge(ViewportInteraction::NudgeDirection::Left,
  646. (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
  647. return true;
  648. case Qt::Key_Right:
  649. Nudge(ViewportInteraction::NudgeDirection::Right,
  650. (ev->modifiers() & Qt::ShiftModifier) ? ViewportInteraction::NudgeSpeed::Fast : ViewportInteraction::NudgeSpeed::Slow);
  651. return true;
  652. default:
  653. break;
  654. }
  655. return false;
  656. }
  657. bool ViewportInteraction::KeyReleaseEvent(QKeyEvent* ev)
  658. {
  659. if (ev->key() == Qt::Key_Space)
  660. {
  661. if (!ev->isAutoRepeat())
  662. {
  663. ClearInteraction();
  664. UpdateCursor();
  665. if (m_editorWindow->GetViewport()->IsInObjectPickMode())
  666. {
  667. // Update hover element right away in case mouse is over an element
  668. QPoint viewportCursorPos = m_editorWindow->GetViewport()->mapFromGlobal(QCursor::pos());
  669. UpdateHoverElement(AZ::Vector2(aznumeric_cast<float>(viewportCursorPos.x()), aznumeric_cast<float>(viewportCursorPos.y())));
  670. }
  671. }
  672. return true;
  673. }
  674. return false;
  675. }
  676. void ViewportInteraction::SetMode(InteractionMode m)
  677. {
  678. ClearInteraction();
  679. m_interactionMode = m;
  680. PersistentSetInteractionMode(m_interactionMode);
  681. m_editorWindow->GetModeToolbar()->SetCheckedItem(static_cast<int>(m_interactionMode));
  682. m_editorWindow->GetModeToolbar()->GetAlignToolbarSection()->SetIsVisible(
  683. m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR);
  684. UpdateCoordinateSystemToolbarSection();
  685. m_editorWindow->GetViewport()->Refresh();
  686. }
  687. ViewportInteraction::InteractionMode ViewportInteraction::GetMode() const
  688. {
  689. return m_interactionMode;
  690. }
  691. ViewportInteraction::InteractionType ViewportInteraction::GetInteractionType() const
  692. {
  693. return m_interactionType;
  694. }
  695. void ViewportInteraction::SetCoordinateSystem(CoordinateSystem s)
  696. {
  697. m_coordinateSystem = s;
  698. PersistentSetCoordinateSystem(s);
  699. m_editorWindow->GetCoordinateSystemToolbarSection()->SetCurrentIndex(static_cast<int>(m_coordinateSystem));
  700. m_editorWindow->GetViewport()->Refresh();
  701. }
  702. ViewportInteraction::CoordinateSystem ViewportInteraction::GetCoordinateSystem() const
  703. {
  704. return m_coordinateSystem;
  705. }
  706. void ViewportInteraction::InitializeToolbars()
  707. {
  708. m_editorWindow->GetModeToolbar()->SetCheckedItem(static_cast<int>(m_interactionMode));
  709. m_editorWindow->GetModeToolbar()->GetAlignToolbarSection()->SetIsVisible(m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR);
  710. m_editorWindow->GetCoordinateSystemToolbarSection()->SetCurrentIndex(static_cast<int>(m_coordinateSystem));
  711. UpdateCoordinateSystemToolbarSection();
  712. bool canvasLoaded = m_editorWindow->GetCanvas().IsValid();
  713. m_editorWindow->GetMainToolbar()->setEnabled(canvasLoaded);
  714. m_editorWindow->GetModeToolbar()->setEnabled(canvasLoaded);
  715. if (!m_editorWindow->GetModeToolbar()->isEnabled())
  716. {
  717. m_editorWindow->GetCoordinateSystemToolbarSection()->SetIsEnabled(false);
  718. }
  719. m_editorWindow->GetEnterPreviewToolbar()->setEnabled(canvasLoaded);
  720. AZ::Vector2 canvasSize(1280.0f, 720.0f);
  721. UiCanvasBus::EventResult(canvasSize, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::GetCanvasSize);
  722. m_editorWindow->GetCanvasSizeToolbarSection()->SetInitialResolution(canvasSize);
  723. if (!m_editorWindow->GetCanvas().IsValid())
  724. {
  725. SetCanvasToViewportScale(1.0f);
  726. }
  727. {
  728. bool isSnapping = false;
  729. UiEditorCanvasBus::EventResult(isSnapping, m_editorWindow->GetCanvas(), &UiEditorCanvasBus::Events::GetIsSnapEnabled);
  730. m_editorWindow->GetCoordinateSystemToolbarSection()->SetSnapToGridIsChecked(isSnapping);
  731. }
  732. if (QToolButton* expansion = AzQtComponents::ToolBar::getToolBarExpansionButton(m_editorWindow->GetMainToolbar()))
  733. {
  734. expansion->installEventFilter(m_expanderWatcher);
  735. }
  736. if (QToolButton* expansion = AzQtComponents::ToolBar::getToolBarExpansionButton(m_editorWindow->GetModeToolbar()))
  737. {
  738. expansion->installEventFilter(m_expanderWatcher);
  739. }
  740. if (QToolButton* expansion = AzQtComponents::ToolBar::getToolBarExpansionButton(m_editorWindow->GetPreviewToolbar()))
  741. {
  742. expansion->installEventFilter(m_expanderWatcher);
  743. }
  744. if (QToolButton* expansion = AzQtComponents::ToolBar::getToolBarExpansionButton(m_editorWindow->GetEnterPreviewToolbar()))
  745. {
  746. expansion->installEventFilter(m_expanderWatcher);
  747. }
  748. }
  749. const ViewportInteraction::TranslationAndScale& ViewportInteraction::GetCanvasViewportMatrixProps()
  750. {
  751. return m_canvasViewportMatrixProps;
  752. }
  753. void ViewportInteraction::SetCanvasViewportMatrixProps(const TranslationAndScale& canvasViewportMatrixProps)
  754. {
  755. m_canvasViewportMatrixProps = canvasViewportMatrixProps;
  756. UpdateCanvasToViewportMatrix();
  757. UpdateShouldScaleToFitOnResize();
  758. }
  759. void ViewportInteraction::CenterCanvasInViewport(const AZ::Vector2* newCanvasSize)
  760. {
  761. GetScaleToFitTransformProps(newCanvasSize, m_canvasViewportMatrixProps);
  762. // Apply scale and translation changes
  763. UpdateCanvasToViewportMatrix();
  764. m_shouldScaleToFitOnViewportResize = true;
  765. }
  766. void ViewportInteraction::GetScaleToFitTransformProps(const AZ::Vector2* newCanvasSize, TranslationAndScale& propsOut)
  767. {
  768. AZ::Vector2 canvasSize;
  769. // Normally we can just get the canvas size from GetCanvasSize, but if the canvas
  770. // size was recently changed, the caller can choose to provide a new canvas size
  771. // so we don't have to wait for the canvas size to update.
  772. if (newCanvasSize)
  773. {
  774. canvasSize = *newCanvasSize;
  775. }
  776. else
  777. {
  778. UiCanvasBus::EventResult(canvasSize, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::GetCanvasSize);
  779. }
  780. AZ::Vector2 viewportSize = m_editorWindow->GetViewport()->GetRenderViewportSize();
  781. const float viewportWidth = viewportSize.GetX();
  782. const float viewportHeight = viewportSize.GetY();
  783. // We pad the edges of the viewport to allow the user to easily see the borders of
  784. // the canvas edges, which is especially helpful if there are anchors sitting on
  785. // the edges of the canvas.
  786. static const int canvasBorderPaddingInPixels = 32;
  787. AZ::Vector2 viewportPaddedSize(
  788. viewportWidth - canvasBorderPaddingInPixels,
  789. viewportHeight - canvasBorderPaddingInPixels);
  790. // Guard against very small viewports
  791. if (viewportPaddedSize.GetX() <= 0.0f)
  792. {
  793. viewportPaddedSize.SetX(viewportWidth);
  794. }
  795. if (viewportPaddedSize.GetY() <= 0.0f)
  796. {
  797. viewportPaddedSize.SetY(viewportHeight);
  798. }
  799. // Use a "scale to fit" approach
  800. const float canvasToViewportScale = AZ::GetMin<float>(
  801. viewportPaddedSize.GetX() / canvasSize.GetX(),
  802. viewportPaddedSize.GetY() / canvasSize.GetY());
  803. const float scaledCanvasWidth = canvasSize.GetX() * canvasToViewportScale;
  804. const float scaledCanvasHeight = canvasSize.GetY() * canvasToViewportScale;
  805. // Centers the canvas within the viewport
  806. propsOut.translation = AZ::Vector3(
  807. 0.5f * (viewportWidth - scaledCanvasWidth),
  808. 0.5f * (viewportHeight - scaledCanvasHeight),
  809. 0.0f);
  810. propsOut.scale = canvasToViewportScale;
  811. }
  812. void ViewportInteraction::DecreaseCanvasToViewportScale()
  813. {
  814. SetCanvasToViewportScale(QuantizeZoomScale(m_canvasViewportMatrixProps.scale - defaultCanvasToViewportScaleIncrement));
  815. }
  816. void ViewportInteraction::IncreaseCanvasToViewportScale()
  817. {
  818. SetCanvasToViewportScale(QuantizeZoomScale(m_canvasViewportMatrixProps.scale + defaultCanvasToViewportScaleIncrement));
  819. }
  820. void ViewportInteraction::ResetCanvasToViewportScale()
  821. {
  822. SetCanvasToViewportScale(1.0f);
  823. }
  824. void ViewportInteraction::SetCanvasZoomPercent(float percent)
  825. {
  826. SetCanvasToViewportScale(percent / 100.0f);
  827. }
  828. void ViewportInteraction::SetCanvasToViewportScale(float newScale, const AZStd::optional<AZ::Vector2>& pivotPoint)
  829. {
  830. // only allow setting the viewport scale if current window is active
  831. // OnTick for ViewportWidget is needed to reevaluate the layout, but does not happen when the window loses focus
  832. if (!m_editorWindow->isActiveWindow())
  833. {
  834. return;
  835. }
  836. static const float minZoom = 0.1f;
  837. static const float maxZoom = 10.0f;
  838. const float currentScale = m_canvasViewportMatrixProps.scale;
  839. m_canvasViewportMatrixProps.scale = AZ::GetClamp(newScale, minZoom, maxZoom);
  840. if (m_editorWindow->GetCanvas().IsValid())
  841. {
  842. // Pivot the zoom based off the center of the viewport's location in canvas space
  843. // Calculate diff between the number of viewport pixels occupied by the current
  844. // scaled canvas view and the new one
  845. AZ::Vector2 canvasSize;
  846. UiCanvasBus::EventResult(canvasSize, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::GetCanvasSize);
  847. const AZ::Vector2 scaledCanvasSize(canvasSize * currentScale);
  848. const AZ::Vector2 newScaledCanvasSize(canvasSize * m_canvasViewportMatrixProps.scale);
  849. const AZ::Vector2 scaledCanvasSizeDiff(newScaledCanvasSize - scaledCanvasSize);
  850. // Get the distance between our pivot point and the upper-left corner of the
  851. // canvas (in viewport space)
  852. const AZ::Vector2 canvasUpperLeft(m_canvasViewportMatrixProps.translation);
  853. // Use the center of our viewport as the pivot point if a pivot is not provided
  854. const AZ::Vector2 pivotDiff = canvasUpperLeft - pivotPoint.value_or(([&]{
  855. const AZ::Vector2 viewportSize = m_editorWindow->GetViewport()->GetRenderViewportSize();
  856. return viewportSize * AZ::Vector2(0.5f, 0.5f);
  857. })());
  858. // Calculate the pivot position relative to the current scaled canvas size. For
  859. // example, if the pivot position is the upper-left corner of the canvas, this
  860. // will be (0, 0), whereas if the pivot position is the bottom-right corner of
  861. // the canvas, this will be (1, 1).
  862. AZ::Vector2 relativePivotPosition(
  863. pivotDiff.GetX() / scaledCanvasSize.GetX(),
  864. pivotDiff.GetY() / scaledCanvasSize.GetY());
  865. // Use the relative pivot position to essentially determine what percentage of
  866. // the difference between the two on-screen canvas sizes should be used to move
  867. // the canvas by to pivot the zoom. For example, if the pivot position is the
  868. // bottom-right corner of the canvas, then we will use 100% of the difference
  869. // in on-screen canvas sizes to move the canvas right and up (to maintain the
  870. // view of the bottom-right corner).
  871. AZ::Vector2 pivotTranslation(
  872. scaledCanvasSizeDiff.GetX() * relativePivotPosition.GetX(),
  873. scaledCanvasSizeDiff.GetY() * relativePivotPosition.GetY());
  874. m_canvasViewportMatrixProps.translation.SetX(m_canvasViewportMatrixProps.translation.GetX() + pivotTranslation.GetX());
  875. m_canvasViewportMatrixProps.translation.SetY(m_canvasViewportMatrixProps.translation.GetY() + pivotTranslation.GetY());
  876. }
  877. UpdateCanvasToViewportMatrix();
  878. UpdateShouldScaleToFitOnResize();
  879. }
  880. float ViewportInteraction::QuantizeZoomScale(float newScale)
  881. {
  882. // Fit to canvas can result in odd zoom scales - when manually zooming we snap it to one of the prefered intervals
  883. // The prefered intervals are in steps of defaultCanvasToViewportScaleIncrement starting at 100% (or a scale of 1.0)
  884. float scaleRelativeto1 = newScale - 1.0f;
  885. float roundedRelative = round(scaleRelativeto1 / defaultCanvasToViewportScaleIncrement) * defaultCanvasToViewportScaleIncrement;
  886. float quantizedScale = roundedRelative + 1.0f;
  887. return quantizedScale;
  888. }
  889. void ViewportInteraction::UpdateZoomFactorLabel()
  890. {
  891. float percentage = m_canvasViewportMatrixProps.scale * 100.0f;
  892. m_editorWindow->GetMainToolbar()->SetZoomPercent(percentage);
  893. }
  894. AZ::Entity* ViewportInteraction::GetActiveElement() const
  895. {
  896. return EntityHelpers::GetEntity(m_activeElementId);
  897. }
  898. const AZ::EntityId& ViewportInteraction::GetActiveElementId() const
  899. {
  900. return m_activeElementId;
  901. }
  902. ViewportHelpers::SelectedAnchors ViewportInteraction::GetGrabbedAnchors() const
  903. {
  904. return m_grabbedAnchors;
  905. }
  906. void ViewportInteraction::UpdateInteractionType(const AZ::Vector2& mousePosition,
  907. const QTreeWidgetItemRawPtrQList& selectedItems)
  908. {
  909. switch (m_interactionMode)
  910. {
  911. case InteractionMode::MOVE:
  912. case InteractionMode::ANCHOR:
  913. {
  914. auto selectedElements = SelectionHelpers::GetSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
  915. if (selectedElements.size() == 1 && m_interactionMode == InteractionMode::ANCHOR)
  916. {
  917. auto selectedElement = selectedElements.front();
  918. if (!ViewportHelpers::IsControlledByLayout(selectedElement) &&
  919. ViewportElement::PickAnchors(selectedElement, mousePosition, m_anchorWhole->GetTextureSize(), m_grabbedAnchors))
  920. {
  921. // Hovering over anchors
  922. m_interactionType = InteractionType::ANCHORS;
  923. m_activeElementId = selectedElement->GetId();
  924. return;
  925. }
  926. }
  927. auto topLevelSelectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
  928. for (auto element : topLevelSelectedElements)
  929. {
  930. if (!ViewportHelpers::IsControlledByLayout(element) &&
  931. ViewportElement::PickAxisGizmo(element, m_coordinateSystem, m_interactionMode, mousePosition, m_lineTriangleX->GetTextureSize(), m_grabbedGizmoParts))
  932. {
  933. // Hovering over move gizmo
  934. m_interactionType = InteractionType::TRANSFORM_GIZMO;
  935. m_activeElementId = element->GetId();
  936. return;
  937. }
  938. }
  939. // if hovering over a guide line, then allow moving it or deleting it by moving out of viewport
  940. if (m_editorWindow->GetViewport()->AreGuidesShown() && !GuideHelpers::AreGuidesLocked(m_editorWindow->GetCanvas()))
  941. {
  942. if (GuideHelpers::PickGuide(m_editorWindow->GetCanvas(), mousePosition, m_activeGuideIsVertical, m_activeGuideIndex))
  943. {
  944. m_interactionType = InteractionType::GUIDE;
  945. m_activeElementId.SetInvalid();
  946. return;
  947. }
  948. }
  949. for (auto element : selectedElements)
  950. {
  951. bool isElementUnderCursor = false;
  952. UiTransformBus::EventResult(isElementUnderCursor, element->GetId(), &UiTransformBus::Events::IsPointInRect, mousePosition);
  953. if (isElementUnderCursor)
  954. {
  955. // Hovering over a selected element
  956. m_interactionType = InteractionType::DIRECT;
  957. m_activeElementId = element->GetId();
  958. return;
  959. }
  960. }
  961. break;
  962. }
  963. case InteractionMode::ROTATE:
  964. {
  965. auto topLevelSelectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
  966. for (auto element : topLevelSelectedElements)
  967. {
  968. if (ViewportElement::PickPivot(element, mousePosition, m_pivotIcon->GetTextureSize()))
  969. {
  970. // Hovering over pivot
  971. m_interactionType = InteractionType::PIVOT;
  972. m_activeElementId = element->GetId();
  973. return;
  974. }
  975. }
  976. for (auto element : topLevelSelectedElements)
  977. {
  978. if (ViewportElement::PickCircleGizmo(element, mousePosition, m_circle->GetTextureSize(), m_grabbedGizmoParts))
  979. {
  980. // Hovering over rotate gizmo
  981. m_interactionType = InteractionType::TRANSFORM_GIZMO;
  982. m_activeElementId = element->GetId();
  983. return;
  984. }
  985. }
  986. break;
  987. }
  988. case InteractionMode::RESIZE:
  989. {
  990. auto topLevelSelectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
  991. for (auto element : topLevelSelectedElements)
  992. {
  993. if (!ViewportHelpers::IsControlledByLayout(element) &&
  994. ViewportElement::PickAxisGizmo(element, CoordinateSystem::LOCAL, m_interactionMode, mousePosition, m_lineTriangleX->GetTextureSize(), m_grabbedGizmoParts))
  995. {
  996. // Hovering over resize gizmo
  997. m_interactionType = InteractionType::TRANSFORM_GIZMO;
  998. m_activeElementId = element->GetId();
  999. return;
  1000. }
  1001. }
  1002. auto selectedElements = SelectionHelpers::GetSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
  1003. for (auto element : selectedElements)
  1004. {
  1005. if (!ViewportHelpers::IsControlledByLayout(element))
  1006. {
  1007. // Check for grabbing element edges
  1008. ViewportElement::PickElementEdges(element, mousePosition, g_elementEdgeForgiveness, m_grabbedEdges);
  1009. if (m_grabbedEdges.BothHorizontal() || m_grabbedEdges.BothVertical())
  1010. {
  1011. // Don't grab both opposite edges
  1012. m_grabbedEdges.SetAll(false);
  1013. }
  1014. if (m_grabbedEdges.Any())
  1015. {
  1016. m_interactionType = InteractionType::DIRECT;
  1017. m_activeElementId = element->GetId();
  1018. return;
  1019. }
  1020. }
  1021. }
  1022. break;
  1023. }
  1024. default:
  1025. {
  1026. // Do nothing
  1027. break;
  1028. }
  1029. } // switch statement
  1030. }
  1031. void ViewportInteraction::UpdateCursor()
  1032. {
  1033. QCursor cursor = Qt::ArrowCursor;
  1034. if (m_spaceBarIsActive)
  1035. {
  1036. cursor = (m_leftButtonIsActive || m_middleButtonIsActive ? Qt::ClosedHandCursor : Qt::OpenHandCursor);
  1037. }
  1038. else if (m_interactionType == InteractionType::GUIDE)
  1039. {
  1040. if (m_activeGuideIsVertical)
  1041. {
  1042. cursor = Qt::SplitHCursor; // vertical guide
  1043. }
  1044. else
  1045. {
  1046. cursor = Qt::SplitVCursor; // horizontal guide
  1047. }
  1048. }
  1049. else if (m_activeElementId.IsValid())
  1050. {
  1051. if ((m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR) &&
  1052. m_interactionType == InteractionType::DIRECT)
  1053. {
  1054. cursor = Qt::SizeAllCursor;
  1055. }
  1056. else if (m_interactionMode == InteractionMode::ROTATE &&
  1057. m_interactionType == InteractionType::TRANSFORM_GIZMO)
  1058. {
  1059. cursor = m_cursorRotate;
  1060. }
  1061. else if (m_interactionMode == InteractionMode::RESIZE &&
  1062. m_interactionType == InteractionType::DIRECT)
  1063. {
  1064. UiTransformInterface::RectPoints rect;
  1065. UiTransformBus::Event(m_activeElementId, &UiTransformBus::Events::GetViewportSpacePoints, rect);
  1066. float topAngle = RAD2DEG(atan2f(rect.TopRight().GetY() - rect.TopLeft().GetY(), rect.TopRight().GetX() - rect.TopLeft().GetX()));
  1067. float leftAngle = RAD2DEG(atan2f(rect.TopLeft().GetY() - rect.BottomLeft().GetY(), rect.TopLeft().GetX() - rect.BottomLeft().GetX()));
  1068. float topLeftAngle = 0.5f * (topAngle + leftAngle);
  1069. float topRightAngle = ViewportHelpers::GetPerpendicularAngle(topLeftAngle);
  1070. if (m_grabbedEdges.TopLeft() || m_grabbedEdges.BottomRight())
  1071. {
  1072. cursor = ViewportHelpers::GetSizingCursor(topLeftAngle);
  1073. }
  1074. else if (m_grabbedEdges.TopRight() || m_grabbedEdges.BottomLeft())
  1075. {
  1076. cursor = ViewportHelpers::GetSizingCursor(topRightAngle);
  1077. }
  1078. else if (m_grabbedEdges.m_left || m_grabbedEdges.m_right)
  1079. {
  1080. cursor = ViewportHelpers::GetSizingCursor(leftAngle);
  1081. }
  1082. else if (m_grabbedEdges.m_top || m_grabbedEdges.m_bottom)
  1083. {
  1084. cursor = ViewportHelpers::GetSizingCursor(topAngle);
  1085. }
  1086. }
  1087. }
  1088. else if (m_editorWindow->GetViewport()->IsInObjectPickMode())
  1089. {
  1090. cursor = m_editorWindow->GetEntityPickerCursor();
  1091. }
  1092. m_editorWindow->GetViewport()->setCursor(cursor);
  1093. }
  1094. void ViewportInteraction::UpdateHoverElement(const AZ::Vector2 mousePosition)
  1095. {
  1096. m_hoverElement.SetInvalid();
  1097. AZ::Entity* element = nullptr;
  1098. UiCanvasBus::EventResult(element, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::PickElement, mousePosition);
  1099. if (element)
  1100. {
  1101. m_hoverElement = element->GetId();
  1102. SetCursorStr(element->GetName());
  1103. }
  1104. else
  1105. {
  1106. SetCursorStr("");
  1107. }
  1108. }
  1109. void ViewportInteraction::InvalidateHoverElement()
  1110. {
  1111. m_hoverElement.SetInvalid();
  1112. SetCursorStr("");
  1113. }
  1114. void ViewportInteraction::SetCursorStr(const AZStd::string& cursorStr)
  1115. {
  1116. m_cursorStr = cursorStr;
  1117. }
  1118. void ViewportInteraction::UpdateCanvasToViewportMatrix()
  1119. {
  1120. AZ::Vector3 scaleVec3(m_canvasViewportMatrixProps.scale, m_canvasViewportMatrixProps.scale, 1.0f);
  1121. AZ::Matrix4x4 updatedMatrix = AZ::Matrix4x4::CreateScale(scaleVec3);
  1122. updatedMatrix.SetTranslation(m_canvasViewportMatrixProps.translation);
  1123. UiCanvasBus::Event(m_editorWindow->GetCanvas(), &UiCanvasBus::Events::SetCanvasToViewportMatrix, updatedMatrix);
  1124. UpdateZoomFactorLabel();
  1125. // when the zoom or pan changes we need to redraw the rulers
  1126. m_editorWindow->GetViewport()->RefreshRulers();
  1127. }
  1128. void ViewportInteraction::UpdateShouldScaleToFitOnResize()
  1129. {
  1130. // If the current viewport matrix props match the "scale to fit" props,
  1131. // the canvas will scale to fit when the viewport resizes.
  1132. TranslationAndScale props;
  1133. GetScaleToFitTransformProps(nullptr, props);
  1134. m_shouldScaleToFitOnViewportResize = (props == m_canvasViewportMatrixProps);
  1135. }
  1136. void ViewportInteraction::ProcessInteraction(const AZ::Vector2& mousePosition,
  1137. Qt::KeyboardModifiers modifiers,
  1138. const QTreeWidgetItemRawPtrQList& selectedItems)
  1139. {
  1140. // Get the mouse move delta, which is in viewport space.
  1141. AZ::Vector2 delta = mousePosition - m_lastMouseDragPos;
  1142. AZ::Vector3 mouseTranslation(delta.GetX(), delta.GetY(), 0.0f);
  1143. BeginReversibleAction(selectedItems);
  1144. bool ctrlIsPressed = modifiers.testFlag(Qt::ControlModifier);
  1145. if (m_interactionType == InteractionType::NONE)
  1146. {
  1147. if (m_isAreaSelectionActive)
  1148. {
  1149. float mouseDragDistance2 = (mousePosition - m_startMouseDragPos).GetLengthSq();
  1150. if (mouseDragDistance2 >= g_minAreaSelectionDistance2)
  1151. {
  1152. // Area selection
  1153. AZ::Vector2 rectMin(min(m_startMouseDragPos.GetX(), mousePosition.GetX()), min(m_startMouseDragPos.GetY(), mousePosition.GetY()));
  1154. AZ::Vector2 rectMax(max(m_startMouseDragPos.GetX(), mousePosition.GetX()), max(m_startMouseDragPos.GetY(), mousePosition.GetY()));
  1155. LyShine::EntityArray elementsToSelect;
  1156. UiCanvasBus::EventResult(
  1157. elementsToSelect, m_editorWindow->GetCanvas(), &UiCanvasBus::Events::PickElements, rectMin, rectMax);
  1158. if (ctrlIsPressed)
  1159. {
  1160. // NOTE: We are fighting against SetSelectedItems a bit here. SetSelectedItems uses Qt
  1161. // to set the selection and the control and shift modifiers affect its behavior.
  1162. // When Ctrl is down, unless you pass null or an empty list it adds to the existing
  1163. // selected items. To get the behavior we want when ctrl is held down we have to clear
  1164. // the selection before setting it. NOTE: if you area select over a group and (during
  1165. // same drag) move the cursor so that they are not in the box then they should not
  1166. // be added to the selection.
  1167. HierarchyHelpers::SetSelectedItem(m_editorWindow->GetHierarchy(), nullptr);
  1168. // when control is pressed we add the selected elements in a drag select to the already selected elements
  1169. // NOTE: It would be nice to allow ctrl-area-select to deselect already selected items. However, the main
  1170. // level editor does not behave that way and we are trying to be consistent (see LMBR-10377)
  1171. for (auto element : m_selectedElementsAtSelectionStart)
  1172. {
  1173. // if not already in the selectedElements then add it
  1174. auto iter = AZStd::find(elementsToSelect.begin(), elementsToSelect.end(), element);
  1175. if (iter == elementsToSelect.end())
  1176. {
  1177. elementsToSelect.push_back(element);
  1178. }
  1179. }
  1180. }
  1181. HierarchyHelpers::SetSelectedItems(m_editorWindow->GetHierarchy(), &elementsToSelect);
  1182. }
  1183. else
  1184. {
  1185. // Selection area too small, ignore
  1186. }
  1187. }
  1188. }
  1189. else if (m_interactionType == InteractionType::PIVOT)
  1190. {
  1191. // Move the pivot that was grabbed
  1192. ViewportElement::MovePivot(m_lastMouseDragPos, EntityHelpers::GetEntity(m_activeElementId), mousePosition);
  1193. }
  1194. else if (m_interactionType == InteractionType::ANCHORS)
  1195. {
  1196. // Move the anchors of the active element
  1197. ViewportElement::MoveAnchors(m_grabbedAnchors, m_startAnchors, m_startMouseDragPos, EntityHelpers::GetEntity(m_activeElementId), mousePosition, ctrlIsPressed);
  1198. }
  1199. else if (m_interactionType == InteractionType::TRANSFORM_GIZMO)
  1200. {
  1201. // Transform all selected elements by interacting with one element's transform gizmo
  1202. switch (m_interactionMode)
  1203. {
  1204. case InteractionMode::MOVE:
  1205. case InteractionMode::ANCHOR:
  1206. if (m_dragInteraction)
  1207. {
  1208. m_dragInteraction->Update(mousePosition);
  1209. }
  1210. break;
  1211. case InteractionMode::ROTATE:
  1212. {
  1213. LyShine::EntityArray selectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
  1214. for (auto element : selectedElements)
  1215. {
  1216. ViewportElement::Rotate(m_editorWindow->GetHierarchy(), m_editorWindow->GetCanvas(), m_lastMouseDragPos, m_activeElementId, element, mousePosition);
  1217. }
  1218. }
  1219. break;
  1220. case InteractionMode::RESIZE:
  1221. {
  1222. if (!ViewportHelpers::IsControlledByLayout(EntityHelpers::GetEntity(m_activeElementId)))
  1223. {
  1224. LyShine::EntityArray selectedElements = SelectionHelpers::GetTopLevelSelectedElements(m_editorWindow->GetHierarchy(), selectedItems);
  1225. for (auto element : selectedElements)
  1226. {
  1227. ViewportElement::ResizeByGizmo(m_editorWindow->GetHierarchy(), m_editorWindow->GetCanvas(), m_grabbedGizmoParts, m_activeElementId, element, mouseTranslation);
  1228. }
  1229. }
  1230. }
  1231. break;
  1232. default:
  1233. AZ_Assert(0, "Unexpected combination of m_interactionMode and m_interactionType.");
  1234. break;
  1235. }
  1236. }
  1237. else if (m_interactionType == InteractionType::DIRECT)
  1238. {
  1239. // Transform all selected elements by interacting with one element directly
  1240. switch (m_interactionMode)
  1241. {
  1242. case InteractionMode::MOVE:
  1243. case InteractionMode::ANCHOR:
  1244. if (m_dragInteraction)
  1245. {
  1246. m_dragInteraction->Update(mousePosition);
  1247. }
  1248. break;
  1249. case InteractionMode::RESIZE:
  1250. // Exception: Direct resizing (grabbing an edge) only affects the element you grabbed
  1251. ViewportElement::ResizeDirectly(m_editorWindow->GetHierarchy(), m_editorWindow->GetCanvas(), m_grabbedEdges, EntityHelpers::GetEntity(m_activeElementId), mouseTranslation);
  1252. break;
  1253. default:
  1254. AZ_Assert(0, "Unexpected combination of m_interactionMode and m_interactionType.");
  1255. break;
  1256. }
  1257. }
  1258. else if (m_interactionType == InteractionType::GUIDE)
  1259. {
  1260. if (m_dragInteraction)
  1261. {
  1262. m_dragInteraction->Update(mousePosition);
  1263. }
  1264. }
  1265. else
  1266. {
  1267. AZ_Assert(0, "Unexpected value for m_interactionType.");
  1268. }
  1269. // Tell the Properties panel to update
  1270. const AZ::Uuid& transformComponentType = InitAndGetTransformComponentType();
  1271. m_editorWindow->GetProperties()->TriggerRefresh(AzToolsFramework::PropertyModificationRefreshLevel::Refresh_Values, &transformComponentType);
  1272. }
  1273. void ViewportInteraction::DrawAxisGizmo(Draw2dHelper& draw2d, const AZ::Entity* element, CoordinateSystem coordinateSystem, const ViewportIcon* lineTextureX, const ViewportIcon* lineTextureY)
  1274. {
  1275. if (UiTransformBus::FindFirstHandler(element->GetId()))
  1276. {
  1277. AZ::Vector2 pivotPosition;
  1278. AZ::Matrix4x4 transform;
  1279. bool isMoveOrAnchorMode = m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR;
  1280. if (coordinateSystem == CoordinateSystem::LOCAL)
  1281. {
  1282. UiTransformBus::EventResult(pivotPosition, element->GetId(), &UiTransformBus::Events::GetCanvasSpacePivotNoScaleRotate);
  1283. // LOCAL MOVE in the parent element's LOCAL space.
  1284. AZ::EntityId elementId(isMoveOrAnchorMode ? EntityHelpers::GetParentElement(element)->GetId() : element->GetId());
  1285. UiTransformBus::Event(elementId, &UiTransformBus::Events::GetTransformToViewport, transform);
  1286. }
  1287. else
  1288. {
  1289. // View coordinate system: do everything in viewport space
  1290. UiTransformBus::EventResult(pivotPosition, element->GetId(), &UiTransformBus::Events::GetViewportSpacePivot);
  1291. transform = AZ::Matrix4x4::CreateIdentity();
  1292. }
  1293. // Draw up axis
  1294. if (isMoveOrAnchorMode || !ViewportHelpers::IsVerticallyFit(element))
  1295. {
  1296. AZ::Color color = ((m_activeElementId == element->GetId()) && m_grabbedGizmoParts.m_top) ? ViewportHelpers::highlightColor : ViewportHelpers::yColor;
  1297. lineTextureY->Draw(draw2d, pivotPosition, transform, 0.0f, color);
  1298. }
  1299. // Draw right axis
  1300. if (isMoveOrAnchorMode || !ViewportHelpers::IsHorizontallyFit(element))
  1301. {
  1302. AZ::Color color = ((m_activeElementId == element->GetId()) && m_grabbedGizmoParts.m_right) ? ViewportHelpers::highlightColor : ViewportHelpers::xColor;
  1303. lineTextureX->Draw(draw2d, pivotPosition, transform, 0.0f, color);
  1304. }
  1305. // Draw center square
  1306. if (isMoveOrAnchorMode || !ViewportHelpers::IsHorizontallyFit(element) && !ViewportHelpers::IsVerticallyFit(element))
  1307. {
  1308. AZ::Color color = ((m_activeElementId == element->GetId()) && m_grabbedGizmoParts.Both()) ? ViewportHelpers::highlightColor : ViewportHelpers::zColor;
  1309. m_centerSquare->Draw(draw2d, pivotPosition, transform, 0.0f, color);
  1310. }
  1311. }
  1312. }
  1313. void ViewportInteraction::DrawCircleGizmo(Draw2dHelper& draw2d, const AZ::Entity* element)
  1314. {
  1315. if (UiTransformBus::FindFirstHandler(element->GetId()))
  1316. {
  1317. AZ::Vector2 pivotPosition;
  1318. UiTransformBus::EventResult(pivotPosition, element->GetId(), &UiTransformBus::Events::GetViewportSpacePivot);
  1319. // Draw circle
  1320. AZ::Color color = ((m_activeElementId == element->GetId()) && m_interactionType == InteractionType::TRANSFORM_GIZMO) ? ViewportHelpers::highlightColor : ViewportHelpers::zColor;
  1321. m_circle->Draw(draw2d, pivotPosition, AZ::Matrix4x4::CreateIdentity(), 0.0f, color);
  1322. }
  1323. }
  1324. void ViewportInteraction::UpdateCoordinateSystemToolbarSection()
  1325. {
  1326. // the coordinate system toolbar should only be enabled in move or anchor mode
  1327. bool isMoveOrAnchorMode = m_interactionMode == InteractionMode::MOVE || m_interactionMode == InteractionMode::ANCHOR;
  1328. m_editorWindow->GetCoordinateSystemToolbarSection()->SetIsEnabled(isMoveOrAnchorMode);
  1329. }
  1330. #include <moc_ViewportInteraction.cpp>