LUAEditorBreakpointWidget.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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 <AzCore/std/string/conversions.h>
  9. #include "LUAEditorBreakpointWidget.hxx"
  10. #include "LUAEditorPlainTextEdit.hxx"
  11. #include "LUAEditorStyleMessages.h"
  12. #include <Source/LUA/moc_LUAEditorBreakpointWidget.cpp>
  13. #include <QTextBlockFormat>
  14. #include <QTextBlock>
  15. #include <QTextBlockUserData>
  16. #include <QPainter>
  17. namespace LUAEditor
  18. {
  19. namespace
  20. {
  21. //this used to detect deleted lines, doesnt work for last line though, but that can be detected elsewhere.
  22. class OriginalLineNumber
  23. : public QTextBlockUserData
  24. {
  25. public:
  26. AZ_CLASS_ALLOCATOR(OriginalLineNumber, AZ::SystemAllocator);
  27. OriginalLineNumber(int lineNumber, AZStd::function<void(int)> callback)
  28. : m_Callback(callback)
  29. , m_LineNumber(lineNumber) {}
  30. virtual ~OriginalLineNumber()
  31. {
  32. if (m_Callback)
  33. {
  34. m_Callback(m_LineNumber);
  35. }
  36. }
  37. int GetLineNumber() const { return m_LineNumber; }
  38. void CancelLineDelete() { m_Callback.clear(); }
  39. private:
  40. AZStd::function<void(int)> m_Callback;
  41. int m_LineNumber;
  42. };
  43. }
  44. LUAEditorBreakpointWidget::LUAEditorBreakpointWidget(QWidget* pParent)
  45. : QWidget(pParent)
  46. , m_textEdit(nullptr)
  47. , m_currentExecLine(-1)
  48. {
  49. setEnabled(false);
  50. }
  51. LUAEditorBreakpointWidget::~LUAEditorBreakpointWidget()
  52. {
  53. }
  54. void LUAEditorBreakpointWidget::PreDestruction()
  55. {
  56. ClearBreakpoints();
  57. m_textEdit = nullptr;
  58. }
  59. void LUAEditorBreakpointWidget::paintEvent([[maybe_unused]] QPaintEvent* paintEvent)
  60. {
  61. QPainter p(this);
  62. auto colors = AZ::UserSettings::CreateFind<SyntaxStyleSettings>(AZ_CRC_CE("LUA Editor Text Settings"), AZ::UserSettings::CT_GLOBAL);
  63. auto oldPen = p.pen();
  64. auto oldBrush = p.brush();
  65. p.setFont(m_font);
  66. if (isEnabled())
  67. {
  68. p.fillRect(geometry(), colors->GetBreakpointFocusedBackgroundColor());
  69. }
  70. else
  71. {
  72. p.fillRect(geometry(), colors->GetBreakpointUnfocusedBackgroundColor());
  73. }
  74. QFontMetrics metrics(m_font);
  75. int avgCharWidth = metrics.averageCharWidth();
  76. m_textEdit->ForEachVisibleBlock([&](const QTextBlock& block, const QRectF& blockRect)
  77. {
  78. int lineNum = block.blockNumber() + 1; // offset by one because line number starts from 1
  79. AZStd::string lineNumStr;
  80. AZStd::to_string(lineNumStr, lineNum);
  81. QRectF drawRect = blockRect;
  82. drawRect.setLeft(c_borderSize);
  83. drawRect.setRight(c_borderSize + m_numDigits * avgCharWidth);
  84. p.setPen(colors->GetLineNumberColor());
  85. p.drawText(drawRect.toRect(), Qt::AlignRight | Qt::AlignBottom, lineNumStr.c_str());
  86. {
  87. auto centerY = (drawRect.top() + metrics.leading() + drawRect.bottom()) / 2;
  88. auto centerX = (2 * drawRect.right() + avgCharWidth + c_borderSize) / 2;
  89. drawRect.setTop(centerY - (avgCharWidth / 2));
  90. drawRect.setBottom(centerY + (avgCharWidth / 2));
  91. drawRect.setLeft(centerX - (avgCharWidth / 2));
  92. drawRect.setRight(centerX + (avgCharWidth / 2));
  93. }
  94. //breakpoint red dot
  95. if (m_breakpoints.find(lineNum) != m_breakpoints.end())
  96. {
  97. p.setPen(QColor::fromRgb(255, 0, 0));
  98. p.setBrush(QBrush(QColor::fromRgb(255, 0, 0)));
  99. p.drawEllipse(drawRect);
  100. }
  101. //yellow triangle for currently executing line
  102. if (m_currentExecLine == lineNum)
  103. {
  104. const QPointF marker[] = {
  105. {drawRect.right(), drawRect.center().y()},
  106. {drawRect.center().x(), drawRect.top() + 1},
  107. {drawRect.center().x(), drawRect.bottom() - 1}
  108. };
  109. p.setPen(QColor::fromRgb(255, 255, 0));
  110. p.setBrush(QBrush(QColor::fromRgb(255, 255, 0)));
  111. p.drawPolygon(marker, sizeof(marker) / sizeof(QPointF));
  112. }
  113. });
  114. p.setPen(oldPen);
  115. p.setBrush(oldBrush);
  116. }
  117. void LUAEditorBreakpointWidget::AddBreakpoint(int lineNumber)
  118. {
  119. auto block = m_textEdit->document()->findBlockByNumber(lineNumber);
  120. if (block.isValid())
  121. {
  122. if (block.userData() != nullptr)
  123. {
  124. ((OriginalLineNumber*)block.userData())->CancelLineDelete();
  125. }
  126. block.setUserData(aznew OriginalLineNumber(lineNumber, [&](int lineNumber){ m_DeletedBreakpoints.push_back(lineNumber); }));
  127. m_breakpoints.insert(lineNumber);
  128. update();
  129. }
  130. }
  131. void LUAEditorBreakpointWidget::RemoveBreakpoint(int lineNumber)
  132. {
  133. auto numRemoved = m_breakpoints.erase(lineNumber);
  134. if (numRemoved != 0)
  135. {
  136. auto block = m_textEdit->document()->findBlockByNumber(lineNumber);
  137. if (block.isValid())
  138. {
  139. if (block.userData() != nullptr)
  140. {
  141. ((OriginalLineNumber*)block.userData())->CancelLineDelete();
  142. }
  143. block.setUserData(nullptr);
  144. }
  145. }
  146. update();
  147. }
  148. bool LUAEditorBreakpointWidget::HasBreakpoint(int lineNumber) const
  149. {
  150. return m_breakpoints.find(lineNumber) != m_breakpoints.end();
  151. }
  152. void LUAEditorBreakpointWidget::ClearBreakpoints()
  153. {
  154. for (auto& breakpoint : m_breakpoints)
  155. {
  156. auto block = m_textEdit->document()->findBlockByNumber(breakpoint);
  157. if (block.isValid())
  158. {
  159. if (block.userData() != nullptr)
  160. {
  161. ((OriginalLineNumber*)block.userData())->CancelLineDelete();
  162. }
  163. block.setUserData(nullptr);
  164. }
  165. }
  166. m_breakpoints.clear();
  167. update();
  168. }
  169. void LUAEditorBreakpointWidget::mouseReleaseEvent(QMouseEvent* event)
  170. {
  171. auto mousePos = event->localPos();
  172. m_textEdit->ForEachVisibleBlock(
  173. [&](const QTextBlock& block, const QRectF& blockRect)
  174. {
  175. if (mousePos.y() >= blockRect.top() && mousePos.y() <= blockRect.bottom())
  176. {
  177. emit toggleBreakpoint(block.blockNumber() + 1); // offset by one because line number starts from 1
  178. }
  179. });
  180. event->accept();
  181. }
  182. void LUAEditorBreakpointWidget::OnBlockCountChange()
  183. {
  184. if (!m_textEdit)
  185. {
  186. return;
  187. }
  188. AZStd::vector<AZStd::pair<int, int> > movedBreakpoints;
  189. for (auto block = m_textEdit->document()->begin(); block != m_textEdit->document()->end(); block = block.next())
  190. {
  191. if (block.userData() != nullptr)
  192. {
  193. if (block.blockNumber() != ((OriginalLineNumber*)block.userData())->GetLineNumber())
  194. {
  195. movedBreakpoints.push_back({ ((OriginalLineNumber*)block.userData())->GetLineNumber(), block.blockNumber() });
  196. }
  197. }
  198. if (block.userData() != nullptr)
  199. {
  200. ((OriginalLineNumber*)block.userData())->CancelLineDelete();
  201. }
  202. block.setUserData(nullptr);
  203. }
  204. for (auto& breakpoint : m_breakpoints)
  205. {
  206. auto block = m_textEdit->document()->findBlockByNumber(breakpoint);
  207. if (block.isValid())
  208. {
  209. block.setUserData(aznew OriginalLineNumber(block.blockNumber(), [&](int lineNumber){ m_DeletedBreakpoints.push_back(lineNumber); }));
  210. }
  211. }
  212. for (auto& moves : movedBreakpoints)
  213. {
  214. emit breakpointLineMove(moves.first, moves.second);
  215. }
  216. for (auto& deletes : m_DeletedBreakpoints)
  217. {
  218. emit breakpointDelete(deletes);
  219. }
  220. m_DeletedBreakpoints.clear();
  221. UpdateSize();
  222. update();
  223. }
  224. void LUAEditorBreakpointWidget::OnCharsRemoved(int position, int charsRemoved)
  225. {
  226. if (!m_textEdit)
  227. {
  228. return;
  229. }
  230. if (charsRemoved <= 0)
  231. {
  232. return;
  233. }
  234. auto block = m_textEdit->document()->findBlock(position);
  235. if (block.userData() && block.blockNumber() != ((OriginalLineNumber*)block.userData())->GetLineNumber())
  236. {
  237. m_DeletedBreakpoints.push_back(((OriginalLineNumber*)block.userData())->GetLineNumber());
  238. ((OriginalLineNumber*)block.userData())->CancelLineDelete();
  239. block.setUserData(nullptr);
  240. }
  241. }
  242. void LUAEditorBreakpointWidget::SetFont(QFont font)
  243. {
  244. m_font = font;
  245. UpdateSize();
  246. }
  247. void LUAEditorBreakpointWidget::UpdateSize()
  248. {
  249. QFontMetrics metrics(m_font);
  250. auto blockCount = m_textEdit->document()->blockCount();
  251. m_numDigits = 0;
  252. while (blockCount > 0)
  253. {
  254. ++m_numDigits;
  255. blockCount /= 10;
  256. }
  257. setFixedWidth(metrics.averageCharWidth() * (m_numDigits + 1) + 2 * c_borderSize); //+1 for the breakpoint
  258. }
  259. }