NodePropertyDisplayWidget.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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 <qgraphicslinearlayout.h>
  9. #include <qgraphicsscene.h>
  10. #include <Widgets/NodePropertyDisplayWidget.h>
  11. #include <GraphCanvas/Components/NodePropertyDisplay/NodePropertyDisplay.h>
  12. namespace GraphCanvas
  13. {
  14. //////////////////////////////
  15. // NodePropertyDisplayWidget
  16. //////////////////////////////
  17. NodePropertyDisplayWidget::NodePropertyDisplayWidget(QGraphicsItem* parent)
  18. : QGraphicsWidget(parent)
  19. , m_nodePropertyDisplay(nullptr)
  20. , m_layoutItem(nullptr)
  21. , m_disabled(false)
  22. , m_editing(false)
  23. , m_forcedLayout(NodePropertyLayoutState::None)
  24. {
  25. m_layout = new QGraphicsLinearLayout(Qt::Vertical);
  26. setLayout(m_layout);
  27. m_layout->setContentsMargins(0, 0, 0, 0);
  28. setContentsMargins(0, 0, 0, 0);
  29. // Timer used to help manage switching between data slots.
  30. // To avoid situations where you tab to the next widget
  31. // and the editing components vanish because nothing is locked
  32. // and the mouse is off of the node.
  33. m_layoutTimer.setSingleShot(true);
  34. m_layoutTimer.setInterval(0);
  35. m_layoutTimer.stop();
  36. QObject::connect(&m_layoutTimer, &QTimer::timeout, [this]() { this->UpdateLayout(); });
  37. }
  38. NodePropertyDisplayWidget::~NodePropertyDisplayWidget()
  39. {
  40. ClearLayout();
  41. delete m_nodePropertyDisplay;
  42. }
  43. void NodePropertyDisplayWidget::RefreshStyle()
  44. {
  45. if (m_nodePropertyDisplay)
  46. {
  47. m_nodePropertyDisplay->RefreshStyle();
  48. }
  49. }
  50. void NodePropertyDisplayWidget::OnDisplayStateChanged(RootGraphicsItemDisplayState /*oldState*/, RootGraphicsItemDisplayState newState)
  51. {
  52. if (newState == RootGraphicsItemDisplayState::Inspection)
  53. {
  54. if (!m_editing)
  55. {
  56. m_editing = true;
  57. UpdateLayout();
  58. }
  59. }
  60. else if (m_editing)
  61. {
  62. m_editing = false;
  63. UpdateLayout();
  64. }
  65. }
  66. void NodePropertyDisplayWidget::LockEditState(NodePropertyDisplay* propertyDisplay)
  67. {
  68. SceneNotificationBus::Event(propertyDisplay->GetSceneId(), &SceneNotifications::OnNodeIsBeingEdited, true);
  69. m_forceEditSet.insert(propertyDisplay);
  70. }
  71. void NodePropertyDisplayWidget::UnlockEditState(NodePropertyDisplay* propertyDisplay)
  72. {
  73. AZStd::size_t count = m_forceEditSet.erase(propertyDisplay);
  74. // In case we are tabbing between elements, we don't want to update the layout immediately.
  75. if (count > 0 && m_forceEditSet.empty())
  76. {
  77. m_layoutTimer.start();
  78. SceneNotificationBus::Event(propertyDisplay->GetSceneId(), &SceneNotifications::OnNodeIsBeingEdited, false);
  79. }
  80. }
  81. void NodePropertyDisplayWidget::SetNodePropertyDisplay(NodePropertyDisplay* propertyDisplayController)
  82. {
  83. if (m_nodePropertyDisplay != nullptr)
  84. {
  85. ClearDisplay();
  86. }
  87. RootGraphicsItemNotificationBus::Handler::BusDisconnect();
  88. NodePropertiesRequestBus::Handler::BusDisconnect();
  89. NodePropertyRequestBus::Handler::BusDisconnect();
  90. m_nodePropertyDisplay = propertyDisplayController;
  91. if (propertyDisplayController)
  92. {
  93. m_nodePropertyDisplay->UpdateDisplay();
  94. m_nodePropertyDisplay->RefreshStyle();
  95. RootGraphicsItemNotificationBus::Handler::BusConnect(m_nodePropertyDisplay->GetNodeId());
  96. NodePropertiesRequestBus::Handler::BusConnect(m_nodePropertyDisplay->GetNodeId());
  97. NodePropertyRequestBus::Handler::BusConnect(m_nodePropertyDisplay->GetSlotId());
  98. }
  99. RootGraphicsItemDisplayState displayState = RootGraphicsItemDisplayState::Neutral;
  100. RootGraphicsItemRequestBus::EventResult(displayState, m_nodePropertyDisplay->GetNodeId(), &RootGraphicsItemRequests::GetDisplayState);
  101. OnDisplayStateChanged(RootGraphicsItemDisplayState::Neutral, displayState);
  102. const bool updateLayout = true;
  103. UpdateLayout(updateLayout);
  104. }
  105. NodePropertyDisplay* NodePropertyDisplayWidget::GetNodePropertyDisplay() const
  106. {
  107. return m_nodePropertyDisplay;
  108. }
  109. void NodePropertyDisplayWidget::ClearDisplay()
  110. {
  111. ClearLayout();
  112. delete m_nodePropertyDisplay;
  113. m_nodePropertyDisplay = nullptr;
  114. m_layoutItem = nullptr;
  115. }
  116. void NodePropertyDisplayWidget::ForceLayoutState(NodePropertyLayoutState layoutState)
  117. {
  118. if (m_forcedLayout != layoutState)
  119. {
  120. m_forcedLayout = layoutState;
  121. UpdateLayout();
  122. }
  123. }
  124. void NodePropertyDisplayWidget::SetDisabled(bool disabled)
  125. {
  126. if (m_disabled != disabled)
  127. {
  128. m_disabled = disabled;
  129. UpdateLayout();
  130. }
  131. }
  132. void NodePropertyDisplayWidget::ClearLayout()
  133. {
  134. for (int i = m_layout->count() - 1; i >= 0; --i)
  135. {
  136. QGraphicsLayoutItem* layoutItem = m_layout->itemAt(i);
  137. m_layout->removeAt(i);
  138. layoutItem->setParentLayoutItem(nullptr);
  139. }
  140. }
  141. void NodePropertyDisplayWidget::UpdateLayout(bool forceUpdate)
  142. {
  143. bool isForcedEdit = !m_forceEditSet.empty();
  144. if (!forceUpdate && isForcedEdit)
  145. {
  146. return;
  147. }
  148. if (m_nodePropertyDisplay == nullptr)
  149. {
  150. ClearLayout();
  151. return;
  152. }
  153. // Removing the m_layoutItem from the scene needs to be delayed to the next event loop,
  154. // because this method gets called during QGraphicsScene::mouseMoveEvent, which in turn
  155. // generates additional events based on a cached list of items in the scene, so if we
  156. // remove an item from the scene while that is in progress, it can crash since it is
  157. // still internally using a cached list of the scene items.
  158. QTimer::singleShot(0, this, [this, isForcedEdit]() {
  159. // There is only one m_layoutItem in m_layout, so we are ok to clear layout in this timer event
  160. ClearLayout();
  161. // Element here needs to be removed from the scene since removing it from the layout
  162. // doesn't actually remove it from the scene. The end result of this is you get an elements
  163. // which is still being rendered, and acts like it's apart of the layout despite not being
  164. // in the layout.
  165. if (m_layoutItem)
  166. {
  167. QGraphicsScene* graphicsScene = nullptr;
  168. AZ::EntityId sceneId = m_nodePropertyDisplay->GetSceneId();
  169. SceneRequestBus::EventResult(graphicsScene, sceneId, &SceneRequests::AsQGraphicsScene);
  170. if (graphicsScene && m_layoutItem->graphicsItem()->scene() != nullptr)
  171. {
  172. graphicsScene->removeItem(m_layoutItem->graphicsItem());
  173. }
  174. m_layoutItem = nullptr;
  175. }
  176. NodePropertyLayoutState layoutState = m_forcedLayout;
  177. if (layoutState == NodePropertyLayoutState::None)
  178. {
  179. if (m_disabled)
  180. {
  181. layoutState = NodePropertyLayoutState::Disabled;
  182. }
  183. else if (m_editing || isForcedEdit)
  184. {
  185. layoutState = NodePropertyLayoutState::Editing;
  186. }
  187. else
  188. {
  189. layoutState = NodePropertyLayoutState::Display;
  190. }
  191. }
  192. if (m_nodePropertyDisplay)
  193. {
  194. switch (layoutState)
  195. {
  196. case NodePropertyLayoutState::Disabled:
  197. m_layoutItem = m_nodePropertyDisplay->GetDisabledGraphicsLayoutItem();
  198. break;
  199. case NodePropertyLayoutState::Editing:
  200. m_layoutItem = m_nodePropertyDisplay->GetEditableGraphicsLayoutItem();
  201. break;
  202. case NodePropertyLayoutState::Display:
  203. m_layoutItem = m_nodePropertyDisplay->GetDisplayGraphicsLayoutItem();
  204. break;
  205. default:
  206. AZ_Warning("DataSlotLayoutComponent", false, "Unhandled layout case.");
  207. m_layoutItem = m_nodePropertyDisplay->GetDisabledGraphicsLayoutItem();
  208. break;
  209. }
  210. m_layout->addItem(m_layoutItem);
  211. m_layout->setAlignment(m_layoutItem, Qt::AlignCenter);
  212. }
  213. UpdateGeometry();
  214. });
  215. }
  216. void NodePropertyDisplayWidget::UpdateGeometry()
  217. {
  218. if (m_layoutItem)
  219. {
  220. m_layoutItem->updateGeometry();
  221. m_layout->invalidate();
  222. }
  223. }
  224. }