LUAEditorFoldingWidget.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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 "LUAEditorFoldingWidget.hxx"
  9. #include "LUAEditorBlockState.h"
  10. #include "LUAEditorPlainTextEdit.hxx"
  11. #include "LUAEditorStyleMessages.h"
  12. #include <Source/LUA/moc_LUAEditorFoldingWidget.cpp>
  13. #include <QTextBlock>
  14. #include <QStyleOption>
  15. #include <QStyle>
  16. #include <QPainter>
  17. namespace LUAEditor
  18. {
  19. FoldingWidget::FoldingWidget(QWidget* pParent)
  20. : QWidget(pParent)
  21. {
  22. setEnabled(false);
  23. }
  24. FoldingWidget::~FoldingWidget()
  25. {
  26. }
  27. void FoldingWidget::paintEvent([[maybe_unused]] QPaintEvent* paintEvent)
  28. {
  29. auto colors = AZ::UserSettings::CreateFind<SyntaxStyleSettings>(AZ_CRC_CE("LUA Editor Text Settings"), AZ::UserSettings::CT_GLOBAL);
  30. auto cursor = m_textEdit->textCursor();
  31. auto selectedBlock = m_textEdit->document()->findBlock(cursor.position());
  32. int startSelectedFold = 0;
  33. int endSelectedFold = 0;
  34. if (selectedBlock.isValid()) //detect if this block closes a fold
  35. {
  36. QTBlockState blockState;
  37. blockState.m_qtBlockState = selectedBlock.userState();
  38. auto prevBlock = selectedBlock.previous();
  39. if (!blockState.m_blockState.m_uninitialized && prevBlock.isValid())
  40. {
  41. QTBlockState prevBlockState;
  42. prevBlockState.m_qtBlockState = prevBlock.userState();
  43. if (blockState.m_blockState.m_foldLevel < prevBlockState.m_blockState.m_foldLevel)
  44. {
  45. selectedBlock = prevBlock;
  46. }
  47. }
  48. }
  49. if (selectedBlock.isValid())
  50. {
  51. startSelectedFold = selectedBlock.blockNumber();
  52. endSelectedFold = selectedBlock.blockNumber() + 1;
  53. QTBlockState blockState;
  54. blockState.m_qtBlockState = selectedBlock.userState();
  55. if (!blockState.m_blockState.m_uninitialized && blockState.m_blockState.m_foldLevel > 0)
  56. {
  57. auto startBlock = selectedBlock;
  58. while (startBlock.isValid())
  59. {
  60. auto prevBlock = startBlock.previous();
  61. QTBlockState prevBlockState;
  62. prevBlockState.m_qtBlockState = prevBlock.userState();
  63. if (prevBlockState.m_blockState.m_foldLevel >= blockState.m_blockState.m_foldLevel)
  64. {
  65. startBlock = prevBlock;
  66. }
  67. else
  68. {
  69. break;
  70. }
  71. }
  72. startSelectedFold = startBlock.blockNumber();
  73. auto endBlock = selectedBlock;
  74. while (endBlock.isValid())
  75. {
  76. auto nextBlock = endBlock.next();
  77. QTBlockState nextBlockState;
  78. nextBlockState.m_qtBlockState = nextBlock.userState();
  79. if (nextBlockState.m_blockState.m_foldLevel >= blockState.m_blockState.m_foldLevel)
  80. {
  81. endBlock = nextBlock;
  82. }
  83. else //less than, which is included in this fold
  84. {
  85. endBlock = nextBlock;
  86. break;
  87. }
  88. }
  89. endSelectedFold = endBlock.blockNumber() + 1;
  90. }
  91. }
  92. QStyleOption opt;
  93. opt.init(this);
  94. QPainter p(this);
  95. style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
  96. AZ::u32 lastFoldLevel = 0;
  97. bool firstBlock = true;
  98. m_textEdit->ForEachVisibleBlock([&](const QTextBlock& block, const QRectF& blockRect)
  99. {
  100. QTBlockState blockState;
  101. blockState.m_qtBlockState = block.userState();
  102. if (!blockState.m_blockState.m_uninitialized)
  103. {
  104. if (firstBlock)
  105. {
  106. lastFoldLevel = 0;
  107. auto prevBlock = block.previous();
  108. if (prevBlock.isValid())
  109. {
  110. QTBlockState prevBlockState;
  111. prevBlockState.m_qtBlockState = prevBlock.userState();
  112. lastFoldLevel = prevBlockState.m_blockState.m_foldLevel;
  113. }
  114. firstBlock = false;
  115. }
  116. auto centerToBorder = (m_singleSize - 2 * c_borderSize) / 2;
  117. QRect drawRect = blockRect.toRect();
  118. drawRect.setLeft(0);
  119. drawRect.setRight(m_singleSize);
  120. auto oldPen = p.pen();
  121. auto oldBrush = p.brush();
  122. if (block.blockNumber() >= startSelectedFold && block.blockNumber() < endSelectedFold)
  123. {
  124. p.setPen(colors->GetFoldingSelectedColor());
  125. }
  126. else
  127. {
  128. p.setPen(colors->GetFoldingColor());
  129. }
  130. p.setBrush(QBrush(Qt::BrushStyle::NoBrush));
  131. if (blockState.m_blockState.m_foldLevel > lastFoldLevel)
  132. {
  133. auto centerEdge = AZStd::max(m_singleSize / 10, 2); //for center lines, 10% of our size
  134. auto center = drawRect.center();
  135. QRect square(center.x() - centerToBorder, center.y() - centerToBorder, 2 * centerToBorder, 2 * centerToBorder);
  136. p.drawRect(square);
  137. p.drawLine(square.left() + centerEdge, center.y(), square.right() - centerEdge + 1, center.y());
  138. if (blockState.m_blockState.m_folded)
  139. {
  140. p.drawLine(center.x(), square.top() + centerEdge, center.x(), square.bottom() - centerEdge + 1);
  141. }
  142. p.drawLine(center.x(), square.bottom() + 1, center.x(), drawRect.bottom());
  143. if (blockState.m_blockState.m_foldLevel > 1)
  144. {
  145. if (block.blockNumber() == startSelectedFold)
  146. {
  147. p.setPen(colors->GetFoldingColor());
  148. }
  149. p.drawLine(center.x(), drawRect.top(), center.x(), square.top() - 1);
  150. }
  151. }
  152. else if (blockState.m_blockState.m_foldLevel < lastFoldLevel)
  153. {
  154. auto center = drawRect.center();
  155. p.drawLine(center.x(), drawRect.top(), center.x(), center.y());
  156. p.drawLine(center.x(), center.y(), drawRect.right(), center.y());
  157. if (blockState.m_blockState.m_foldLevel > 0)
  158. {
  159. if (block.blockNumber() == endSelectedFold - 1)
  160. {
  161. p.setPen(colors->GetFoldingColor());
  162. }
  163. p.drawLine(center.x(), center.y(), center.x(), drawRect.bottom());
  164. }
  165. }
  166. else if (blockState.m_blockState.m_foldLevel > 0)
  167. {
  168. auto center = drawRect.center();
  169. p.drawLine(center.x(), drawRect.top(), center.x(), drawRect.bottom());
  170. }
  171. lastFoldLevel = blockState.m_blockState.m_foldLevel;
  172. p.setPen(oldPen);
  173. p.setBrush(oldBrush);
  174. }
  175. });
  176. }
  177. void FoldingWidget::mouseReleaseEvent(QMouseEvent* event)
  178. {
  179. auto mousePos = event->localPos();
  180. m_textEdit->ForEachVisibleBlock([&](QTextBlock& blockClicked, const QRectF& blockRect)
  181. {
  182. if (mousePos.y() >= blockRect.top() && mousePos.y() <= blockRect.bottom())
  183. {
  184. auto prevBlock = blockClicked.previous();
  185. QTBlockState state;
  186. state.m_qtBlockState = blockClicked.userState();
  187. AZ::u32 prevFoldLevel = 0;
  188. if (prevBlock.isValid())
  189. {
  190. QTBlockState prevBlockState;
  191. prevBlockState.m_qtBlockState = prevBlock.userState();
  192. prevFoldLevel = prevBlockState.m_blockState.m_foldLevel;
  193. }
  194. if (!state.m_blockState.m_uninitialized && state.m_blockState.m_foldLevel > prevFoldLevel)
  195. {
  196. state.m_blockState.m_folded = 1 - state.m_blockState.m_folded;
  197. blockClicked.setUserState(state.m_qtBlockState);
  198. int startDirty = blockClicked.position();
  199. int dirtyLength = (blockClicked.position() + blockClicked.length()) - startDirty;
  200. auto nextBlock = blockClicked.next();
  201. QTBlockState nextState;
  202. nextState.m_qtBlockState = nextBlock.userState();
  203. bool currentChildFolded = false;
  204. AZ::u32 currentChildFoldLevel = state.m_blockState.m_foldLevel;
  205. while (nextBlock.isValid() && nextState.m_blockState.m_foldLevel >= state.m_blockState.m_foldLevel)
  206. {
  207. if (state.m_blockState.m_folded)
  208. {
  209. nextBlock.setVisible(false);
  210. }
  211. else // we are unfolding, need to preserve any child folds that were already folded before we were
  212. {
  213. if (!currentChildFolded)
  214. {
  215. nextBlock.setVisible(true);
  216. if (nextState.m_blockState.m_foldLevel > currentChildFoldLevel)
  217. {
  218. currentChildFoldLevel = nextState.m_blockState.m_foldLevel;
  219. if (nextState.m_blockState.m_folded)
  220. {
  221. currentChildFolded = true;
  222. }
  223. }
  224. }
  225. else
  226. {
  227. bool visible = false;
  228. if (nextState.m_blockState.m_foldLevel < currentChildFoldLevel)
  229. {
  230. currentChildFolded = false;
  231. visible = true;
  232. }
  233. nextBlock.setVisible(visible);
  234. }
  235. }
  236. dirtyLength = (nextBlock.position() + nextBlock.length()) - startDirty;
  237. nextBlock = nextBlock.next();
  238. nextState.m_qtBlockState = nextBlock.userState();
  239. }
  240. update();
  241. m_textEdit->document()->markContentsDirty(startDirty, dirtyLength);
  242. TextBlockFoldingChanged();
  243. }
  244. }
  245. });
  246. event->accept();
  247. }
  248. void FoldingWidget::OnContentChanged(int from, int, int charsAdded)
  249. {
  250. auto startBlock = m_textEdit->document()->findBlock(from);
  251. if (!startBlock.isValid())
  252. {
  253. return;
  254. }
  255. auto endBlock = m_textEdit->document()->findBlock(from + charsAdded);
  256. if (!endBlock.isValid())
  257. {
  258. endBlock = startBlock;
  259. }
  260. auto prevBlock = startBlock.previous();
  261. bool atLeastOne = false;
  262. while (prevBlock.isValid() && !prevBlock.isVisible())
  263. {
  264. startBlock = prevBlock;
  265. prevBlock = prevBlock.previous();
  266. atLeastOne = true;
  267. }
  268. if (atLeastOne) //need to grab the opening fold
  269. {
  270. startBlock = prevBlock;
  271. }
  272. auto nextBlock = endBlock.next();
  273. while (nextBlock.isValid() && !nextBlock.isVisible())
  274. {
  275. endBlock = nextBlock;
  276. nextBlock = nextBlock.next();
  277. }
  278. int startDirty = startBlock.position();
  279. int dirtyLength = (endBlock.position() + endBlock.length()) - startDirty;
  280. while (startBlock.isValid() && startBlock.blockNumber() <= endBlock.blockNumber())
  281. {
  282. QTBlockState state;
  283. state.m_qtBlockState = startBlock.userState();
  284. if (state.m_blockState.m_folded)
  285. {
  286. state.m_blockState.m_folded = 0;
  287. startBlock.setUserState(state.m_qtBlockState);
  288. }
  289. startBlock.setVisible(true);
  290. startBlock = startBlock.next();
  291. }
  292. update();
  293. m_textEdit->document()->markContentsDirty(startDirty, dirtyLength);
  294. TextBlockFoldingChanged();
  295. }
  296. void FoldingWidget::FoldAll()
  297. {
  298. AZ::u32 lastFoldLevel = 0;
  299. for (auto block = m_textEdit->document()->begin(); block != m_textEdit->document()->end(); block = block.next())
  300. {
  301. block.setVisible(true);
  302. QTBlockState state;
  303. state.m_qtBlockState = block.userState();
  304. if (state.m_blockState.m_foldLevel > lastFoldLevel)
  305. {
  306. state.m_blockState.m_folded = 1;
  307. block.setUserState(state.m_qtBlockState);
  308. if (lastFoldLevel != 0)
  309. {
  310. block.setVisible(false);
  311. }
  312. }
  313. else if (state.m_blockState.m_foldLevel > 0)
  314. {
  315. block.setVisible(false);
  316. }
  317. lastFoldLevel = state.m_blockState.m_foldLevel;
  318. }
  319. update();
  320. m_textEdit->document()->markContentsDirty(0, m_textEdit->document()->characterCount());
  321. TextBlockFoldingChanged();
  322. }
  323. void FoldingWidget::UnfoldAll()
  324. {
  325. for (auto block = m_textEdit->document()->begin(); block != m_textEdit->document()->end(); block = block.next())
  326. {
  327. block.setVisible(true);
  328. QTBlockState state;
  329. state.m_qtBlockState = block.userState();
  330. state.m_blockState.m_folded = 0;
  331. block.setUserState(state.m_qtBlockState);
  332. }
  333. update();
  334. m_textEdit->document()->markContentsDirty(0, m_textEdit->document()->characterCount());
  335. TextBlockFoldingChanged();
  336. }
  337. void FoldingWidget::SetFont(QFont font)
  338. {
  339. QFontMetrics metrics(font);
  340. m_singleSize = metrics.height();
  341. setFixedWidth(m_singleSize);
  342. }
  343. }