GraphCanvasLabel.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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 <QCoreApplication>
  9. #include <QFont>
  10. #include <QGraphicsItem>
  11. #include <QGraphicsSceneEvent>
  12. #include <QPainter>
  13. #include <qwidget.h>
  14. #include <AzCore/Serialization/EditContext.h>
  15. #include <Widgets/GraphCanvasLabel.h>
  16. #include <GraphCanvas/Editor/GraphCanvasProfiler.h>
  17. #include <GraphCanvas/tools.h>
  18. #include <GraphCanvas/Styling/StyleHelper.h>
  19. #include <Translation/TranslationBus.h>
  20. namespace GraphCanvas
  21. {
  22. /////////////////////
  23. // GraphCanvasLabel
  24. /////////////////////
  25. GraphCanvasLabel::GraphCanvasLabel(QGraphicsItem* parent)
  26. : QGraphicsWidget(parent)
  27. , m_defaultAlignment(Qt::AlignVCenter | Qt::AlignHCenter)
  28. , m_elide(true)
  29. , m_wrap(false)
  30. , m_allowNewlines(false)
  31. , m_maximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)
  32. , m_minimumSize(0,0)
  33. , m_wrapMode(WrapMode::MaximumWidth)
  34. , m_roundedCornersMode(RoundedCornersMode::AllCorners)
  35. , m_hasBorderOverride(false)
  36. {
  37. setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  38. setGraphicsItem(this);
  39. setFlag(ItemIsMovable, false);
  40. }
  41. void GraphCanvasLabel::SetFontColor(const QColor& color)
  42. {
  43. m_styleHelper.AddAttributeOverride(Styling::Attribute::Color, color);
  44. update();
  45. }
  46. void GraphCanvasLabel::ClearFontColor()
  47. {
  48. m_styleHelper.RemoveAttributeOverride(Styling::Attribute::Color);
  49. update();
  50. }
  51. void GraphCanvasLabel::SetBorderColorOverride(const QBrush& brush)
  52. {
  53. m_hasBorderOverride = true;
  54. m_borderColorOverride = brush;
  55. update();
  56. }
  57. const QBrush& GraphCanvasLabel::GetBorderColorOverride() const
  58. {
  59. return m_borderColorOverride;
  60. }
  61. void GraphCanvasLabel::ClearBorderColorOverride()
  62. {
  63. m_hasBorderOverride = false;
  64. }
  65. void GraphCanvasLabel::SetLabel(const AZStd::string& value)
  66. {
  67. if (m_labelText.compare(QString(value.c_str())))
  68. {
  69. m_labelText = Tools::qStringFromUtf8(value);
  70. UpdateDisplayText();
  71. RefreshDisplay();
  72. }
  73. }
  74. void GraphCanvasLabel::SetSceneStyle(const AZ::EntityId& sceneId, const char* style)
  75. {
  76. m_styleHelper.SetScene(sceneId);
  77. m_styleHelper.SetStyle(style);
  78. UpdateDisplayText();
  79. RefreshDisplay();
  80. }
  81. void GraphCanvasLabel::SetStyle(const AZ::EntityId& entityId, const char* styleElement)
  82. {
  83. m_styleHelper.SetStyle(entityId, styleElement);
  84. UpdateDisplayText();
  85. RefreshDisplay();
  86. }
  87. void GraphCanvasLabel::RefreshDisplay()
  88. {
  89. UpdateDesiredBounds();
  90. updateGeometry();
  91. update();
  92. }
  93. void GraphCanvasLabel::SetWrapMode(WrapMode wrapMode)
  94. {
  95. if (m_wrapMode != wrapMode)
  96. {
  97. m_wrapMode = wrapMode;
  98. UpdateDesiredBounds();
  99. UpdateDisplayText();
  100. RefreshDisplay();
  101. }
  102. }
  103. void GraphCanvasLabel::SetRoundedCornersMode(RoundedCornersMode roundedCornersMode)
  104. {
  105. if (m_roundedCornersMode != roundedCornersMode)
  106. {
  107. m_roundedCornersMode = roundedCornersMode;
  108. update();
  109. }
  110. }
  111. QRectF GraphCanvasLabel::GetDisplayedSize() const
  112. {
  113. return m_displayedSize;
  114. }
  115. void GraphCanvasLabel::SetElide(bool elide)
  116. {
  117. if (m_elide != elide)
  118. {
  119. m_elide = elide;
  120. RefreshDisplay();
  121. }
  122. }
  123. void GraphCanvasLabel::SetWrap(bool wrap)
  124. {
  125. if (m_wrap != wrap)
  126. {
  127. m_wrap = wrap;
  128. RefreshDisplay();
  129. }
  130. }
  131. void GraphCanvasLabel::SetAllowNewlines(bool allow)
  132. {
  133. if (m_allowNewlines != allow)
  134. {
  135. m_allowNewlines = allow;
  136. UpdateDisplayText();
  137. RefreshDisplay();
  138. }
  139. }
  140. void GraphCanvasLabel::SetDefaultAlignment(Qt::Alignment defaultAlignment)
  141. {
  142. m_defaultAlignment = defaultAlignment;
  143. update();
  144. }
  145. Styling::StyleHelper& GraphCanvasLabel::GetStyleHelper()
  146. {
  147. return m_styleHelper;
  148. }
  149. const Styling::StyleHelper& GraphCanvasLabel::GetStyleHelper() const
  150. {
  151. return m_styleHelper;
  152. }
  153. void GraphCanvasLabel::UpdateDisplayText()
  154. {
  155. qreal padding = m_styleHelper.GetAttribute(Styling::Attribute::Padding, 2.0f);
  156. QFontMetrics metrics(m_styleHelper.GetFont());
  157. QRectF innerBounds = boundingRect();
  158. innerBounds = innerBounds.adjusted(padding, padding, -padding, -padding);
  159. QString labelText = m_allowNewlines ? m_labelText : m_labelText.simplified();
  160. if (m_elide)
  161. {
  162. QStringList newlines = labelText.split('\n');
  163. m_displayText.clear();
  164. bool needsNewline = false;
  165. for (const QString& currentLine : newlines)
  166. {
  167. if (needsNewline)
  168. {
  169. m_displayText.append('\n');
  170. }
  171. m_displayText = m_displayText.append(metrics.elidedText(currentLine, Qt::TextElideMode::ElideRight, aznumeric_cast<int>(innerBounds.width())));
  172. needsNewline = true;
  173. }
  174. }
  175. else
  176. {
  177. m_displayText = labelText;
  178. }
  179. }
  180. void GraphCanvasLabel::UpdateDesiredBounds()
  181. {
  182. prepareGeometryChange();
  183. qreal padding = m_styleHelper.GetAttribute(Styling::Attribute::Padding, 2.0f);
  184. QFontMetricsF metrics = QFontMetricsF(m_styleHelper.GetFont());
  185. int flags = m_defaultAlignment;
  186. if (m_wrap)
  187. {
  188. flags = flags | Qt::TextWordWrap;
  189. }
  190. flags = flags & ~Qt::TextSingleLine;
  191. m_maximumSize = m_styleHelper.GetMaximumSize();
  192. QRectF fontRectangle;
  193. if (m_wrapMode == WrapMode::ResizeToContent)
  194. {
  195. fontRectangle = metrics.boundingRect((m_allowNewlines) ? m_labelText : m_labelText.simplified());
  196. }
  197. else
  198. {
  199. QSizeF sizeClamp = maximumSize();
  200. if (m_wrapMode == WrapMode::BoundingWidth)
  201. {
  202. sizeClamp.setWidth(boundingRect().size().width());
  203. }
  204. QRectF maxInnerBounds(rect().topLeft(), sizeClamp);
  205. maxInnerBounds = maxInnerBounds.adjusted(padding, padding, -padding, -padding);
  206. fontRectangle = metrics.boundingRect(maxInnerBounds, flags, (m_allowNewlines) ? m_labelText : m_labelText.simplified());
  207. }
  208. // Horizontal Padding:
  209. // 1 padding for the left side
  210. // 1 padding for the right side
  211. //
  212. // Vertical Padding:
  213. // 1 padding for the top
  214. // 1 padding for the bottom.
  215. m_desiredBounds = fontRectangle.adjusted(0.0f, 0.0f, padding * 2.0f, padding * 2.0f);
  216. // Seem so be an off by 1 error here. So adding one in each direction to my desired size to stop text from being clipped.
  217. m_desiredBounds.adjust(-1.0, -1.0, 1.0, 1.0);
  218. m_minimumSize = m_styleHelper.GetMinimumSize(m_desiredBounds.size());
  219. if (m_wrapMode == WrapMode::ResizeToContent)
  220. {
  221. // Because the minimum is usually what drives this. We want to ensure the minimum is the bigger
  222. // of the value style or the desired bounds.
  223. if (m_minimumSize.width() < m_desiredBounds.width())
  224. {
  225. m_minimumSize.setWidth(m_desiredBounds.width() + 5);
  226. }
  227. // Maximum Size should act normally and just be the bigger of either its own value or the desired bounds.
  228. if (m_maximumSize.width() < m_desiredBounds.width())
  229. {
  230. m_maximumSize.setWidth(m_desiredBounds.width() + 5);
  231. }
  232. }
  233. updateGeometry();
  234. setCacheMode(QGraphicsItem::CacheMode::DeviceCoordinateCache);
  235. }
  236. bool GraphCanvasLabel::event(QEvent* qEvent)
  237. {
  238. if (qEvent->type() == QEvent::GraphicsSceneResize)
  239. {
  240. UpdateDisplayText();
  241. }
  242. return QGraphicsWidget::event(qEvent);
  243. }
  244. void GraphCanvasLabel::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget /*= nullptr*/)
  245. {
  246. GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
  247. AZ_Warning("GraphCanvasLabel", !(m_elide && m_wrap), "GraphCanvasLabel doesn't support eliding text and word wrapping at the same time.");
  248. painter->save();
  249. // Background
  250. {
  251. m_displayedSize = boundingRect();
  252. QPainterPath path;
  253. qreal borderRadius = m_styleHelper.GetAttribute(Styling::Attribute::BorderRadius, 0);
  254. if (borderRadius == 0)
  255. {
  256. path.addRect(m_displayedSize);
  257. }
  258. else
  259. {
  260. switch (m_roundedCornersMode)
  261. {
  262. case RoundedCornersMode::AllCorners:
  263. {
  264. path.addRoundedRect(m_displayedSize, borderRadius, borderRadius);
  265. }
  266. break;
  267. case RoundedCornersMode::LeftCorners:
  268. {
  269. painter->setClipRect(m_displayedSize);
  270. path.addRoundedRect(m_displayedSize.x(),
  271. m_displayedSize.y(),
  272. m_displayedSize.width() + borderRadius * 2,
  273. m_displayedSize.height(),
  274. borderRadius,
  275. borderRadius);
  276. }
  277. break;
  278. case RoundedCornersMode::RightCorners:
  279. {
  280. painter->setClipRect(m_displayedSize);
  281. path.addRoundedRect(m_displayedSize.x() - borderRadius * 2,
  282. m_displayedSize.y(),
  283. m_displayedSize.width() + borderRadius * 2,
  284. m_displayedSize.height(),
  285. borderRadius,
  286. borderRadius);
  287. }
  288. break;
  289. default:
  290. {
  291. AZ_Warning("GraphCanvasLabel", 0, "GraphCanvasLabel has an unsupported rounded corner mode.");
  292. path.addRoundedRect(m_displayedSize, borderRadius, borderRadius);
  293. }
  294. break;
  295. }
  296. }
  297. painter->fillPath(path, m_styleHelper.GetBrush(Styling::Attribute::BackgroundColor));
  298. if (m_styleHelper.HasAttribute(Styling::Attribute::BorderWidth) || m_hasBorderOverride)
  299. {
  300. QPen restorePen = painter->pen();
  301. QPen borderPen = m_styleHelper.GetBorder();
  302. if (m_hasBorderOverride)
  303. {
  304. borderPen.setBrush(m_borderColorOverride);
  305. }
  306. painter->setPen(borderPen);
  307. painter->drawPath(path);
  308. painter->setPen(restorePen);
  309. }
  310. }
  311. // Text.
  312. if (!m_labelText.isEmpty())
  313. {
  314. qreal padding = m_styleHelper.GetAttribute(Styling::Attribute::Padding, 4.0f);
  315. QRectF innerBounds = m_displayedSize;
  316. innerBounds = innerBounds.adjusted(padding, padding, -padding, -padding);
  317. painter->setPen(m_styleHelper.GetColor(Styling::Attribute::Color));
  318. painter->setBrush(QBrush());
  319. painter->setFont(m_styleHelper.GetFont());
  320. Qt::Alignment textAlignment = m_styleHelper.GetTextAlignment(m_defaultAlignment);
  321. QTextOption opt(textAlignment);
  322. opt.setFlags(QTextOption::IncludeTrailingSpaces);
  323. if (m_wrap)
  324. {
  325. opt.setWrapMode(QTextOption::WordWrap);
  326. }
  327. else
  328. {
  329. opt.setWrapMode(QTextOption::NoWrap);
  330. }
  331. painter->drawText(innerBounds, m_displayText, opt);
  332. }
  333. painter->restore();
  334. QGraphicsWidget::paint(painter, option, widget);
  335. }
  336. QSizeF GraphCanvasLabel::sizeHint(Qt::SizeHint which, const QSizeF& constraint /*= QSizeF()*/) const
  337. {
  338. switch (which)
  339. {
  340. case Qt::MinimumSize:
  341. return m_minimumSize;
  342. case Qt::PreferredSize:
  343. return m_desiredBounds.size();
  344. case Qt::MaximumSize:
  345. return m_maximumSize;
  346. default:
  347. return QGraphicsWidget::sizeHint(which, constraint);
  348. }
  349. }
  350. }