ConsoleSCB.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419
  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 "EditorDefs.h"
  9. #include "ConsoleSCB.h"
  10. // Qt
  11. #include <QHeaderView>
  12. #include <QSortFilterProxyModel>
  13. #include <QtWidgets/QMenu>
  14. #include <QtWidgets/QScrollBar>
  15. #include <QtWidgets/QTableView>
  16. #include <QtGui/QSyntaxHighlighter>
  17. // AzQtComponents
  18. #include <AzQtComponents/Components/StyledLineEdit.h>
  19. #include <AzQtComponents/Components/StyleManager.h>
  20. #include <AzQtComponents/Components/Widgets/LineEdit.h>
  21. #include <AzQtComponents/Components/Widgets/ScrollBar.h>
  22. #include <AzQtComponents/Components/Widgets/SliderCombo.h>
  23. // Editor
  24. #include "QtViewPaneManager.h"
  25. #include "Core/QtEditorApplication.h"
  26. #include "Commands/CommandManager.h"
  27. #include "Util/Variable.h"
  28. #include "CvarDPE.h"
  29. #include <AzToolsFramework/UI/DocumentPropertyEditor/DocumentPropertyEditor.h>
  30. static void OnVariableUpdated(ICVar* pCVar);
  31. AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
  32. #include <Controls/ui_ConsoleSCB.h>
  33. AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
  34. namespace ConsoleConstants
  35. {
  36. static constexpr const char* ButtonIcon = ":/controls/img/cvar_dark.bmp";
  37. static constexpr const char* SearchIcon = ":/stylesheet/img/search.svg";
  38. static constexpr const char* ClearIcon = ":/stylesheet/img/lineedit-clear.png";
  39. static constexpr const char* MenuIcon = ":/Menu/menu.svg";
  40. } // namespace ConsoleConstants
  41. class CConsoleSCB::SearchHighlighter : public QSyntaxHighlighter
  42. {
  43. public:
  44. SearchHighlighter(QObject *parent)
  45. : QSyntaxHighlighter(parent)
  46. {
  47. }
  48. SearchHighlighter(QTextDocument *parent)
  49. : QSyntaxHighlighter(parent)
  50. {
  51. }
  52. void setSearchTerm(const QString &term)
  53. {
  54. m_searchTerm = term;
  55. rehighlight();
  56. }
  57. protected:
  58. void highlightBlock(const QString &text) override
  59. {
  60. auto pos = -1;
  61. QTextCharFormat myClassFormat;
  62. myClassFormat.setFontWeight(QFont::Bold);
  63. myClassFormat.setBackground(Qt::yellow);
  64. while (true)
  65. {
  66. pos = text.indexOf(m_searchTerm, pos+1, Qt::CaseInsensitive);
  67. if (pos == -1)
  68. {
  69. break;
  70. }
  71. setFormat(pos, m_searchTerm.length(), myClassFormat);
  72. }
  73. }
  74. private:
  75. QString m_searchTerm;
  76. };
  77. static CConsoleSCB* s_consoleSCB = nullptr;
  78. // Constant for the modified console variable color
  79. static const QColor g_modifiedConsoleVariableColor(243, 129, 29);
  80. namespace {
  81. enum Column
  82. {
  83. ColumnType,
  84. ColumnName,
  85. ColumnValue,
  86. ColumnCount // keep at end, for iteration purposes
  87. };
  88. }
  89. static QString RemoveColorCode(const QString& text, int& iColorCode)
  90. {
  91. QString cleanString;
  92. cleanString.reserve(text.size());
  93. const int textSize = text.size();
  94. for (int i = 0; i < textSize; ++i)
  95. {
  96. QChar c = text.at(i);
  97. bool isLast = i == textSize - 1;
  98. if (c == '$' && !isLast && text.at(i + 1).isDigit())
  99. {
  100. if (iColorCode == 0)
  101. {
  102. iColorCode = text.at(i + 1).digitValue();
  103. }
  104. ++i;
  105. continue;
  106. }
  107. // convert \r \n to just \n
  108. if (c == '\r')
  109. {
  110. ++i;
  111. continue;
  112. }
  113. cleanString.append(c);
  114. }
  115. return cleanString;
  116. }
  117. ConsoleLineEdit::ConsoleLineEdit(QWidget* parent)
  118. : QLineEdit(parent)
  119. , m_historyIndex(0)
  120. , m_bReusedHistory(false)
  121. {
  122. }
  123. void ConsoleLineEdit::mouseDoubleClickEvent([[maybe_unused]] QMouseEvent* ev)
  124. {
  125. Q_EMIT variableEditorRequested();
  126. }
  127. bool ConsoleLineEdit::event(QEvent* ev)
  128. {
  129. // Tab key doesn't go to keyPressEvent(), must be processed here
  130. if (ev->type() != QEvent::KeyPress)
  131. {
  132. return QLineEdit::event(ev);
  133. }
  134. QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
  135. if (ke->key() != Qt::Key_Tab)
  136. {
  137. return QLineEdit::event(ev);
  138. }
  139. QString inputStr = text();
  140. QString newStr;
  141. QStringList tokens = inputStr.split(" ");
  142. inputStr = tokens.isEmpty() ? QString() : tokens.first();
  143. IConsole* console = GetIEditor()->GetSystem()->GetIConsole();
  144. const bool ctrlPressed = ke->modifiers() & Qt::ControlModifier;
  145. QString cstring = inputStr;
  146. if (ctrlPressed)
  147. {
  148. newStr = console->AutoCompletePrev(cstring.toUtf8().data());
  149. }
  150. else
  151. {
  152. newStr = console->ProcessCompletion(cstring.toUtf8().data());
  153. newStr = console->AutoComplete(cstring.toUtf8().data());
  154. if (newStr.isEmpty())
  155. {
  156. newStr = GetIEditor()->GetCommandManager()->AutoComplete(cstring.toUtf8().data()).c_str();
  157. }
  158. }
  159. if (!newStr.isEmpty())
  160. {
  161. newStr += " ";
  162. setText(newStr);
  163. }
  164. deselect();
  165. return true;
  166. }
  167. void ConsoleLineEdit::keyPressEvent(QKeyEvent* ev)
  168. {
  169. IConsole* console = GetIEditor()->GetSystem()->GetIConsole();
  170. auto commandManager = GetIEditor()->GetCommandManager();
  171. console->ResetAutoCompletion();
  172. switch (ev->key())
  173. {
  174. case Qt::Key_Enter:
  175. case Qt::Key_Return:
  176. {
  177. QString str = text().trimmed();
  178. if (!str.isEmpty())
  179. {
  180. if (commandManager->IsRegistered(str.toUtf8().data()))
  181. {
  182. commandManager->Execute(str.toUtf8().data());
  183. }
  184. else
  185. {
  186. CLogFile::WriteLine(str.toUtf8().data());
  187. GetIEditor()->GetSystem()->GetIConsole()->ExecuteString(str.toUtf8().data());
  188. }
  189. // If a history command was reused directly via up arrow enter, do not reset history index
  190. if (m_history.size() > 0 && m_historyIndex < static_cast<unsigned int>(m_history.size()) && m_history[m_historyIndex] == str)
  191. {
  192. m_bReusedHistory = true;
  193. }
  194. else
  195. {
  196. ResetHistoryIndex();
  197. }
  198. // Do not add the same string if it is the top of the stack, but allow duplicate entries otherwise
  199. if (m_history.isEmpty() || m_history.back() != str)
  200. {
  201. m_history.push_back(str);
  202. if (!m_bReusedHistory)
  203. {
  204. ResetHistoryIndex();
  205. }
  206. }
  207. }
  208. else
  209. {
  210. ResetHistoryIndex();
  211. }
  212. setText(QString());
  213. break;
  214. }
  215. case Qt::Key_AsciiTilde: // ~
  216. case Qt::Key_Agrave: // `
  217. // disable log.
  218. GetIEditor()->ShowConsole(false);
  219. setText(QString());
  220. ResetHistoryIndex();
  221. break;
  222. case Qt::Key_Escape:
  223. setText(QString());
  224. ResetHistoryIndex();
  225. break;
  226. case Qt::Key_Up:
  227. DisplayHistory(false /*bForward*/);
  228. break;
  229. case Qt::Key_Down:
  230. DisplayHistory(true /*bForward*/);
  231. break;
  232. default:
  233. QLineEdit::keyPressEvent(ev);
  234. }
  235. }
  236. void ConsoleLineEdit::DisplayHistory(bool bForward)
  237. {
  238. if (m_history.isEmpty())
  239. {
  240. return;
  241. }
  242. const int increment = bForward ? 1
  243. : m_bReusedHistory ? 0 // Immediately after reusing a history entry, ensure up arrow re-displays command just used
  244. : -1;
  245. const int newHistoryIndex = static_cast<int>(m_historyIndex) + increment;
  246. m_bReusedHistory = false;
  247. m_historyIndex = static_cast<unsigned int>(clamp_tpl(newHistoryIndex, 0, m_history.size() - 1));
  248. setText(m_history[m_historyIndex]);
  249. }
  250. void ConsoleLineEdit::ResetHistoryIndex()
  251. {
  252. m_historyIndex = m_history.size();
  253. m_bReusedHistory = false;
  254. }
  255. Lines CConsoleSCB::s_pendingLines;
  256. CConsoleSCB::CConsoleSCB(QWidget* parent)
  257. : QWidget(parent)
  258. , ui(new Ui::Console())
  259. , m_backgroundTheme(gSettings.consoleBackgroundColorTheme)
  260. {
  261. m_lines = s_pendingLines;
  262. s_pendingLines.clear();
  263. s_consoleSCB = this;
  264. ui->setupUi(this);
  265. m_highlighter = new SearchHighlighter(ui->textEdit);
  266. m_highlighter->setDocument(ui->textEdit->document());
  267. setMinimumHeight(120);
  268. ui->findBar->setVisible(false);
  269. ui->lineEditFind->setPlaceholderText(QObject::tr("Search..."));
  270. ui->lineEditFind->setClearButtonEnabled(true);
  271. AzQtComponents::LineEdit::applySearchStyle(ui->lineEditFind);
  272. SetupOptionsMenu();
  273. // Setup the color table for the default (light) theme
  274. m_colorTable << QColor(0, 0, 0)
  275. << QColor(0, 0, 0)
  276. << QColor(0, 0, 200) // blue
  277. << QColor(0, 200, 0) // green
  278. << QColor(200, 0, 0) // red
  279. << QColor(0, 200, 200) // cyan
  280. << QColor(128, 112, 0) // yellow
  281. << QColor(200, 0, 200) // red+blue
  282. << QColor(0x000080ff)
  283. << QColor(0x008f8f8f);
  284. RefreshStyle();
  285. auto findNextAction = new QAction(this);
  286. findNextAction->setShortcut(QKeySequence::FindNext);
  287. connect(findNextAction, &QAction::triggered, this, &CConsoleSCB::findNext);
  288. ui->findNextButton->addAction(findNextAction);
  289. auto findPreviousAction = new QAction(this);
  290. findPreviousAction->setShortcut(QKeySequence::FindPrevious);
  291. connect(findPreviousAction, &QAction::triggered, this, &CConsoleSCB::findPrevious);
  292. ui->findPrevButton->addAction(findPreviousAction);
  293. GetIEditor()->RegisterNotifyListener(this);
  294. connect(ui->button, &QPushButton::clicked, this, &CConsoleSCB::showVariableEditor);
  295. connect(ui->findButton, &QPushButton::clicked, this, &CConsoleSCB::toggleConsoleSearch);
  296. connect(ui->textEdit, &AzToolsFramework::ConsoleTextEdit::searchBarRequested, this, [this]
  297. {
  298. this->ui->findBar->setVisible(true);
  299. this->ui->lineEditFind->setFocus();
  300. });
  301. connect(ui->lineEditFind, &QLineEdit::returnPressed, this, &CConsoleSCB::findNext);
  302. connect(ui->closeButton, &QPushButton::clicked, [=]
  303. {
  304. ui->findBar->setVisible(false);
  305. });
  306. connect(ui->findPrevButton, &QPushButton::clicked, this, &CConsoleSCB::findPrevious);
  307. connect(ui->findNextButton, &QPushButton::clicked, this, &CConsoleSCB::findNext);
  308. connect(ui->lineEditFind, &QLineEdit::textChanged, [=](auto text)
  309. {
  310. m_highlighter->setSearchTerm(text);
  311. });
  312. connect(ui->lineEdit, &ConsoleLineEdit::variableEditorRequested, this, &CConsoleSCB::showVariableEditor);
  313. AzToolsFramework::EditorPreferencesNotificationBus::Handler::BusConnect();
  314. }
  315. CConsoleSCB::~CConsoleSCB()
  316. {
  317. AzToolsFramework::EditorPreferencesNotificationBus::Handler::BusDisconnect();
  318. GetIEditor()->UnregisterNotifyListener(this);
  319. s_consoleSCB = nullptr;
  320. CLogFile::AttachEditBox(nullptr);
  321. }
  322. void CConsoleSCB::SetupOptionsMenu()
  323. {
  324. m_optionsMenu = new QMenu(QStringLiteral("Console Options Menu"), this);
  325. connect(m_optionsMenu, &QMenu::aboutToShow, this, &CConsoleSCB::UpdateOptionsMenu);
  326. ui->optionsButton->setMenu(m_optionsMenu);
  327. ui->optionsButton->setAutoRaise(true);
  328. ui->optionsButton->setPopupMode(QToolButton::InstantPopup);
  329. m_clearOnPlayAction = new QAction(tr("Clear On Play"), this);
  330. m_clearOnPlayAction->setCheckable(true);
  331. connect(m_clearOnPlayAction, &QAction::triggered, this, &CConsoleSCB::toggleClearOnPlay);
  332. m_optionsMenu->addAction(m_clearOnPlayAction);
  333. }
  334. void CConsoleSCB::UpdateOptionsMenu()
  335. {
  336. m_clearOnPlayAction->setChecked(gSettings.clearConsoleOnGameModeStart);
  337. }
  338. void CConsoleSCB::toggleClearOnPlay()
  339. {
  340. gSettings.clearConsoleOnGameModeStart = !gSettings.clearConsoleOnGameModeStart;
  341. }
  342. void CConsoleSCB::RegisterViewClass()
  343. {
  344. AzToolsFramework::ViewPaneOptions opts;
  345. opts.preferedDockingArea = Qt::BottomDockWidgetArea;
  346. opts.isDeletable = false;
  347. opts.isStandard = true;
  348. opts.showInMenu = true;
  349. opts.builtInActionId = ID_VIEW_CONSOLEWINDOW;
  350. opts.shortcut = QKeySequence(Qt::Key_QuoteLeft);
  351. // Override the default behavior for component mode enter/exit and imgui enter/exit
  352. // so that we don't disable and enable the Console window.
  353. opts.isDisabledInComponentMode = false;
  354. opts.isDisabledInImGuiMode = false;
  355. AzToolsFramework::RegisterViewPane<CConsoleSCB>(LyViewPane::Console, LyViewPane::CategoryTools, opts);
  356. }
  357. void CConsoleSCB::OnEditorPreferencesChanged()
  358. {
  359. RefreshStyle();
  360. }
  361. void CConsoleSCB::RefreshStyle()
  362. {
  363. ui->button->setIcon(QIcon(ConsoleConstants::ButtonIcon));
  364. ui->findButton->setIcon(QIcon(ConsoleConstants::SearchIcon));
  365. ui->closeButton->setIcon(QIcon(ConsoleConstants::ClearIcon));
  366. ui->optionsButton->setIcon(QIcon(ConsoleConstants::MenuIcon));
  367. // Set the debug/warning text colors appropriately for the background theme
  368. // (e.g. not have black text on black background)
  369. QColor textColor = Qt::black;
  370. m_colorTable[4] = QColor(200, 0, 0); // Error (Red)
  371. m_colorTable[6] = QColor(128, 112, 0); // Warning (Yellow)
  372. m_backgroundTheme = gSettings.consoleBackgroundColorTheme;
  373. if (m_backgroundTheme == AzToolsFramework::ConsoleColorTheme::Dark)
  374. {
  375. textColor = Qt::white;
  376. m_colorTable[4] = QColor(0xfa, 0x27, 0x27); // Error (Red)
  377. m_colorTable[6] = QColor(0xff, 0xaa, 0x22); // Warning (Yellow)
  378. }
  379. QColor bgColor;
  380. if (!GetIEditor()->IsInConsolewMode() && CConsoleSCB::GetCreatedInstance() && m_backgroundTheme == AzToolsFramework::ConsoleColorTheme::Dark)
  381. {
  382. bgColor = QColor(0x22, 0x22, 0x22);
  383. AzQtComponents::ScrollBar::applyLightStyle(ui->textEdit);
  384. }
  385. else
  386. {
  387. bgColor = Qt::white;
  388. textColor = Qt::black;
  389. AzQtComponents::ScrollBar::applyDarkStyle(ui->textEdit);
  390. }
  391. m_colorTable[0] = textColor;
  392. m_colorTable[1] = textColor;
  393. ui->textEdit->setStyleSheet(QString("background: %1").arg(bgColor.name(QColor::HexRgb)));
  394. // Clear out the console text when we change our background color since
  395. // some of the previous text colors may not be appropriate for the
  396. // new background color
  397. QString text = ui->textEdit->toPlainText();
  398. ui->textEdit->clear();
  399. m_lines.push_back({ text, false });
  400. FlushText();
  401. }
  402. void CConsoleSCB::SetInputFocus()
  403. {
  404. ui->lineEdit->setFocus();
  405. ui->lineEdit->setText(QString());
  406. }
  407. void CConsoleSCB::AddToConsole(const QString& text, bool bNewLine)
  408. {
  409. m_lines.push_back({ text, bNewLine });
  410. FlushText();
  411. }
  412. void CConsoleSCB::FlushText()
  413. {
  414. if (m_lines.empty())
  415. {
  416. return;
  417. }
  418. // Store our current cursor in case we need to restore it, and check if
  419. // the user has scrolled the text edit away from the bottom
  420. const QTextCursor oldCursor = ui->textEdit->textCursor();
  421. QScrollBar* scrollBar = ui->textEdit->verticalScrollBar();
  422. const int oldScrollValue = scrollBar->value();
  423. bool scrolledOffBottom = oldScrollValue != scrollBar->maximum();
  424. ui->textEdit->moveCursor(QTextCursor::End);
  425. QTextCursor textCursor = ui->textEdit->textCursor();
  426. while (!m_lines.empty())
  427. {
  428. ConsoleLine line = m_lines.front();
  429. m_lines.pop_front();
  430. int iColor = 0;
  431. QString text = RemoveColorCode(line.text, iColor);
  432. if (iColor < 0 || iColor >= m_colorTable.size())
  433. {
  434. iColor = 0;
  435. }
  436. if (line.newLine)
  437. {
  438. text = QtUtil::trimRight(text);
  439. text = "\n" + text;
  440. }
  441. QTextCharFormat format;
  442. const QColor color(m_colorTable[iColor]);
  443. format.setForeground(color);
  444. if (iColor != 0)
  445. {
  446. format.setFontWeight(QFont::Bold);
  447. }
  448. textCursor.setCharFormat(format);
  449. textCursor.insertText(text);
  450. }
  451. // If the user has selected some text in the text edit area or has scrolled
  452. // away from the bottom, then restore the previous cursor and keep the scroll
  453. // bar in the same location
  454. if (oldCursor.hasSelection() || scrolledOffBottom)
  455. {
  456. ui->textEdit->setTextCursor(oldCursor);
  457. scrollBar->setValue(oldScrollValue);
  458. }
  459. // Otherwise scroll to the bottom so the latest text can be seen
  460. else
  461. {
  462. scrollBar->setValue(scrollBar->maximum());
  463. ui->textEdit->moveCursor(QTextCursor::StartOfLine);
  464. }
  465. }
  466. QSize CConsoleSCB::minimumSizeHint() const
  467. {
  468. return QSize(-1, -1);
  469. }
  470. QSize CConsoleSCB::sizeHint() const
  471. {
  472. return QSize(100, 100);
  473. }
  474. /** static */
  475. void CConsoleSCB::AddToPendingLines(const QString& text, bool bNewLine)
  476. {
  477. s_pendingLines.push_back({ text, bNewLine });
  478. }
  479. /**
  480. * When a CVar variable is updated, we need to tell alert our console variables
  481. * pane so it can update the corresponding row
  482. */
  483. static void OnVariableUpdated(ICVar* pCVar)
  484. {
  485. QtViewPane* pane = QtViewPaneManager::instance()->GetPane(LyViewPane::ConsoleVariables);
  486. if (!pane)
  487. {
  488. return;
  489. }
  490. ConsoleVariableEditor* variableEditor = qobject_cast<ConsoleVariableEditor*>(pane->Widget());
  491. if (!variableEditor)
  492. {
  493. return;
  494. }
  495. variableEditor->HandleVariableRowUpdated(pCVar);
  496. }
  497. static CVarBlock* VarBlockFromConsoleVars()
  498. {
  499. IConsole* console = GetIEditor()->GetSystem()->GetIConsole();
  500. AZStd::vector<AZStd::string_view> cmds;
  501. cmds.resize(console->GetNumVars());
  502. size_t cmdCount = console->GetSortedVars(cmds);
  503. CVarBlock* vb = new CVarBlock;
  504. IVariable* pVariable = nullptr;
  505. for (int i = 0; i < cmdCount; i++)
  506. {
  507. if (!cmds[i].data())
  508. {
  509. continue;
  510. }
  511. ICVar* pCVar = console->GetCVar(cmds[i].data());
  512. if (!pCVar)
  513. {
  514. continue;
  515. }
  516. int varType = pCVar->GetType();
  517. switch (varType)
  518. {
  519. case CVAR_INT:
  520. pVariable = new CVariable<int>();
  521. pVariable->Set(pCVar->GetIVal());
  522. break;
  523. case CVAR_FLOAT:
  524. pVariable = new CVariable<float>();
  525. pVariable->Set(pCVar->GetFVal());
  526. break;
  527. case CVAR_STRING:
  528. pVariable = new CVariable<QString>();
  529. pVariable->Set(pCVar->GetString());
  530. break;
  531. default:
  532. assert(0);
  533. }
  534. pVariable->SetDescription(pCVar->GetHelp());
  535. pVariable->SetName(cmds[i].data());
  536. // Transfer the custom limits have they have been set for this variable
  537. if (pCVar->HasCustomLimits())
  538. {
  539. float min, max;
  540. pCVar->GetLimits(min, max);
  541. pVariable->SetLimits(min, max);
  542. }
  543. if (pVariable)
  544. {
  545. vb->AddVariable(pVariable);
  546. }
  547. }
  548. return vb;
  549. }
  550. static void OnConsoleVariableUpdated(IVariable* pVar)
  551. {
  552. if (!pVar)
  553. {
  554. return;
  555. }
  556. QString varName = pVar->GetName();
  557. ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(varName.toUtf8().data());
  558. if (!pCVar)
  559. {
  560. return;
  561. }
  562. if (pVar->GetType() == IVariable::INT)
  563. {
  564. int val;
  565. pVar->Get(val);
  566. pCVar->Set(val);
  567. }
  568. else if (pVar->GetType() == IVariable::FLOAT)
  569. {
  570. float val;
  571. pVar->Get(val);
  572. pCVar->Set(val);
  573. }
  574. else if (pVar->GetType() == IVariable::STRING)
  575. {
  576. QString val;
  577. pVar->Get(val);
  578. pCVar->Set(val.toUtf8().data());
  579. }
  580. }
  581. ConsoleVariableItemDelegate::ConsoleVariableItemDelegate(QObject* parent)
  582. : QStyledItemDelegate(parent)
  583. , m_varBlock(nullptr)
  584. {
  585. }
  586. /**
  587. * This method is called when after an editor widget has been created for an item
  588. * in the model to set its initial value
  589. */
  590. void ConsoleVariableItemDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
  591. {
  592. if (auto* doubleEditor = qobject_cast<AzQtComponents::SliderDoubleCombo*>(editor))
  593. {
  594. // If this is a float variable, we need to set the decimal precision of
  595. // our editor to fit the precision of the variable's default value
  596. QVariant value = index.data();
  597. IVariable* var = index.data(ConsoleVariableModel::VariableCustomRole).value<IVariable*>();
  598. Q_ASSERT(var->GetType() == IVariable::FLOAT);
  599. QString valStr = QString::number(value.toFloat());
  600. int decimalIndex = valStr.indexOf('.');
  601. if (decimalIndex != -1)
  602. {
  603. valStr.remove(0, decimalIndex + 1);
  604. doubleEditor->setDecimals(valStr.size());
  605. }
  606. // Set the initial value to our editor
  607. doubleEditor->setValue(value.toDouble());
  608. }
  609. else if (auto* intEditor = qobject_cast<AzQtComponents::SliderCombo*>(editor))
  610. {
  611. QVariant value = index.data();
  612. IVariable* var = index.data(ConsoleVariableModel::VariableCustomRole).value<IVariable*>();
  613. Q_ASSERT(var->GetType() == IVariable::INT);
  614. // Set the initial value to our editor
  615. intEditor->setValue(value.toInt());
  616. }
  617. // Otherwise the value is a string, so the editor will be our styled line edit
  618. else
  619. {
  620. AzQtComponents::StyledLineEdit* lineEdit = qobject_cast<AzQtComponents::StyledLineEdit*>(editor);
  621. if (!lineEdit)
  622. {
  623. return;
  624. }
  625. lineEdit->setText(index.data().toString());
  626. }
  627. }
  628. /**
  629. * This method is called when the editor widget has triggered to write back a value to the model
  630. */
  631. void ConsoleVariableItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
  632. {
  633. if (auto* doubleEditor = qobject_cast<AzQtComponents::SliderDoubleCombo*>(editor))
  634. {
  635. model->setData(index, doubleEditor->value());
  636. }
  637. else if (auto* intEditor = qobject_cast<AzQtComponents::SliderCombo*>(editor))
  638. {
  639. model->setData(index, intEditor->value());
  640. }
  641. else if (auto* lineEdit = qobject_cast<AzQtComponents::StyledLineEdit*>(editor))
  642. {
  643. model->setData(index, lineEdit->text());
  644. }
  645. }
  646. template <typename EditorType>
  647. static void SetEditorRange(EditorType* editor, IVariable* var)
  648. {
  649. // Retrieve the limits set for the variable
  650. float min;
  651. float max;
  652. float step;
  653. bool hardMin;
  654. bool hardMax;
  655. var->GetLimits(min, max, step, hardMin, hardMax);
  656. // If this variable has custom limits set, then use that as the min/max
  657. // Otherwise, the min/max for the input box will be bounded by the type
  658. // limit, but the slider will be constricted to a smaller default range
  659. static const float defaultMin = -100.0f;
  660. static const float defaultMax = 100.0f;
  661. if (var->HasCustomLimits())
  662. {
  663. editor->setRange(static_cast<typename EditorType::value_type>(min), static_cast<typename EditorType::value_type>(max));
  664. }
  665. else
  666. {
  667. editor->setSoftRange(static_cast<typename EditorType::value_type>(defaultMin), static_cast<typename EditorType::value_type>(defaultMax));
  668. }
  669. // Set the step size. The default variable step is 0, so if it's
  670. // not set then set our step size to 0.1 for float variables.
  671. // The default step size for our spin box is 1.0 so we can just
  672. // use that for the int values
  673. if (step > 0)
  674. {
  675. editor->spinbox()->setSingleStep(static_cast<int>(step));
  676. }
  677. else if (auto doubleSpinBox = qobject_cast<AzQtComponents::DoubleSpinBox*>(editor->spinbox()))
  678. {
  679. doubleSpinBox->setSingleStep(0.1);
  680. }
  681. }
  682. /**
  683. * This method is called when the user tries to edit one of the values in the
  684. * table so that a widget can be created to use for editing the value
  685. */
  686. QWidget* ConsoleVariableItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
  687. {
  688. // This shouldn't happen, but just in case this gets called before we've
  689. // been given a var block, just let the item delegate create a default
  690. // editor for the value
  691. if (!m_varBlock)
  692. {
  693. return QStyledItemDelegate::createEditor(parent, option, index);
  694. }
  695. // Retrieve a reference to our IVariable for the specified index
  696. IVariable* var = index.data(ConsoleVariableModel::VariableCustomRole).value<IVariable*>();
  697. if (var)
  698. {
  699. // Create the proper editor for the int or float value
  700. QWidget* editor = nullptr;
  701. QVariant value = index.data();
  702. // Use the IVariable type; it's more stable than Qt's
  703. IVariable::EType type = var->GetType();
  704. bool hasCustomLimits = var->HasCustomLimits();
  705. if (type == IVariable::INT)
  706. {
  707. // We need to make sure this is casted to the regular StyledSpinBox
  708. // instead of the StyledDoubleSpinBox base class because when we
  709. // set the min/max it has overridden that method to update the validator
  710. auto* intEditor = new AzQtComponents::SliderCombo(parent);
  711. editor = intEditor;
  712. // If this variable doesn't have custom limits set, use the type min/max
  713. if (!hasCustomLimits)
  714. {
  715. intEditor->setMinimum(INT_MIN);
  716. intEditor->setMaximum(INT_MAX);
  717. }
  718. SetEditorRange(intEditor, var);
  719. }
  720. else if (type == IVariable::FLOAT)
  721. {
  722. auto* doubleEditor = new AzQtComponents::SliderDoubleCombo(parent);
  723. editor = doubleEditor;
  724. // If this variable doesn't have custom limits set, use the integer
  725. // type min/max because if we use the DBL_MIN/MAX the minimum will
  726. // be interpreted as 0
  727. if (!hasCustomLimits)
  728. {
  729. doubleEditor->setMinimum(INT_MIN);
  730. doubleEditor->setMaximum(INT_MAX);
  731. }
  732. SetEditorRange(doubleEditor, var);
  733. }
  734. if (editor)
  735. {
  736. // Set the given geometry
  737. editor->setGeometry(option.rect);
  738. return editor;
  739. }
  740. }
  741. // If we get here, value being edited is a string, so use our styled line
  742. // edit widget
  743. AzQtComponents::StyledLineEdit* lineEdit = new AzQtComponents::StyledLineEdit(parent);
  744. lineEdit->setGeometry(option.rect);
  745. return lineEdit;
  746. }
  747. void ConsoleVariableItemDelegate::SetVarBlock(CVarBlock* varBlock)
  748. {
  749. m_varBlock = varBlock;
  750. }
  751. ConsoleVariableModel::ConsoleVariableModel(QObject* parent)
  752. : QAbstractTableModel(parent)
  753. , m_varBlock(nullptr)
  754. {
  755. qRegisterMetaType<IVariable*>("IVariable");
  756. }
  757. QVariant ConsoleVariableModel::data(const QModelIndex& index, int role) const
  758. {
  759. if (index.row() < 0 || index.row() >= rowCount() || index.column() < 0 || index.column() >= ColumnCount)
  760. {
  761. return QVariant();
  762. }
  763. IVariable* var = m_varBlock->GetVariable(index.row());
  764. if (!var)
  765. {
  766. return QVariant();
  767. }
  768. // Return data for the display and edit roles for when the table data is
  769. // displayed and when a user is attempting to edit a value
  770. const int col = index.column();
  771. IVariable::EType type = var->GetType();
  772. if (role == Qt::DisplayRole || role == Qt::EditRole)
  773. {
  774. switch (col)
  775. {
  776. case ColumnType:
  777. if (type == IVariable::STRING)
  778. {
  779. return QString("ab");
  780. }
  781. else
  782. {
  783. return QString("n");
  784. }
  785. case ColumnName:
  786. return QString(var->GetName());
  787. case ColumnValue:
  788. if (type == IVariable::INT)
  789. {
  790. int value;
  791. var->Get(value);
  792. return value;
  793. }
  794. else if (type == IVariable::FLOAT)
  795. {
  796. float value;
  797. var->Get(value);
  798. return value;
  799. }
  800. else // string
  801. {
  802. QString value;
  803. var->Get(value);
  804. return value;
  805. }
  806. }
  807. }
  808. // Set the tooltip role for the text to display when the mouse is hovered
  809. // over a row in the table
  810. else if (role == Qt::ToolTipRole)
  811. {
  812. QString typeName;
  813. switch (type)
  814. {
  815. case IVariable::INT:
  816. typeName = tr("Int");
  817. break;
  818. case IVariable::FLOAT:
  819. typeName = tr("Float");
  820. break;
  821. case IVariable::STRING:
  822. typeName = tr("String");
  823. break;
  824. }
  825. QString toolTip = QString("[%1] %2 = %3\n%4").arg(type).arg(var->GetName()).arg(QString(var->GetDisplayValue())).arg(var->GetDescription());
  826. return toolTip;
  827. }
  828. // Set a different text color for the row if its value has been modified
  829. else if (role == Qt::ForegroundRole)
  830. {
  831. if (m_modifiedRows.indexOf(index.row()) != -1)
  832. {
  833. return g_modifiedConsoleVariableColor;
  834. }
  835. }
  836. // Change the text alignment just for the type column to be right/center
  837. // so that it looks cleaner (the name/value columns are left/center by default)
  838. else if (role == Qt::TextAlignmentRole && col == ColumnType)
  839. {
  840. int alignment = Qt::AlignRight | Qt::AlignVCenter;
  841. return alignment;
  842. }
  843. // Make the text in the type column bold
  844. else if (role == Qt::FontRole && col == ColumnType)
  845. {
  846. QFont font;
  847. font.setBold(true);
  848. return font;
  849. }
  850. else if (role == VariableCustomRole)
  851. {
  852. return QVariant::fromValue(var);
  853. }
  854. return QVariant();
  855. }
  856. bool ConsoleVariableModel::setData(const QModelIndex& index, const QVariant& value, int role)
  857. {
  858. // Don't continue if the index is invalid, we aren't in edit mode, or if the
  859. // new value isn't different than the current value
  860. if (!index.isValid() || role != Qt::EditRole || (index.data() == value))
  861. {
  862. return false;
  863. }
  864. const int row = index.row();
  865. IVariable* var = m_varBlock->GetVariable(row);
  866. if (!var)
  867. {
  868. return false;
  869. }
  870. // Attempt to set new values for our int/float/string variables
  871. bool ok = false;
  872. switch (var->GetType())
  873. {
  874. case IVariable::INT:
  875. {
  876. int intValue = value.toInt(&ok);
  877. if (ok)
  878. {
  879. var->Set(intValue);
  880. }
  881. break;
  882. }
  883. case IVariable::FLOAT:
  884. {
  885. float floatValue = value.toFloat(&ok);
  886. if (ok)
  887. {
  888. var->Set(floatValue);
  889. }
  890. break;
  891. }
  892. case IVariable::STRING:
  893. {
  894. ok = true;
  895. QString strValue = value.toString();
  896. var->Set(strValue);
  897. break;
  898. }
  899. }
  900. // Update our data if the variable set was successful
  901. if (ok)
  902. {
  903. // Update the actual cvar
  904. OnConsoleVariableUpdated(var);
  905. // Emit this signal so the model knows to update
  906. emit dataChanged(index, index);
  907. // Add this row to our list of modified rows so we can change its text color
  908. m_modifiedRows.append(row);
  909. return true;
  910. }
  911. return false;
  912. }
  913. int ConsoleVariableModel::rowCount(const QModelIndex& parent) const
  914. {
  915. if (parent.isValid() || !m_varBlock)
  916. {
  917. return 0;
  918. }
  919. return m_varBlock->GetNumVariables();
  920. }
  921. int ConsoleVariableModel::columnCount(const QModelIndex& parent) const
  922. {
  923. if (parent.isValid())
  924. {
  925. return 0;
  926. }
  927. return ColumnCount;
  928. }
  929. Qt::ItemFlags ConsoleVariableModel::flags(const QModelIndex& index) const
  930. {
  931. // Can select any row, but can only edit the value column
  932. Qt::ItemFlags itemFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  933. if (index.column() == ColumnValue)
  934. {
  935. itemFlags |= Qt::ItemIsEditable;
  936. }
  937. return itemFlags;
  938. }
  939. QVariant ConsoleVariableModel::headerData(int section, Qt::Orientation orientation, int role) const
  940. {
  941. if (section < 0 || section >= ColumnCount || orientation == Qt::Vertical)
  942. {
  943. return QVariant();
  944. }
  945. return QAbstractTableModel::headerData(section, orientation, role);
  946. }
  947. void ConsoleVariableModel::SetVarBlock(CVarBlock* varBlock)
  948. {
  949. beginResetModel();
  950. m_varBlock = varBlock;
  951. endResetModel();
  952. }
  953. void ConsoleVariableModel::ClearModifiedRows()
  954. {
  955. m_modifiedRows.clear();
  956. }
  957. template<typename T, int expectedCvarType>
  958. static bool SetCVarFromConsoleCommand(ICVar* cvar, AZ::ConsoleFunctorBase* consoleCommand)
  959. {
  960. bool succeeded = false;
  961. if (T value; consoleCommand->GetValue(value) == AZ::GetValueResult::Success)
  962. {
  963. if (cvar->GetType() == expectedCvarType)
  964. {
  965. if constexpr (expectedCvarType == CVAR_INT)
  966. {
  967. cvar->Set(static_cast<int>(value));
  968. succeeded = true;
  969. }
  970. else if constexpr (expectedCvarType == CVAR_FLOAT)
  971. {
  972. cvar->Set(static_cast<float>(value));
  973. succeeded = true;
  974. }
  975. else if constexpr (expectedCvarType == CVAR_STRING)
  976. {
  977. if (!value.empty())
  978. {
  979. cvar->Set(value.data());
  980. succeeded = true;
  981. }
  982. }
  983. }
  984. else if (cvar->GetType() == CVAR_STRING)
  985. {
  986. if constexpr (expectedCvarType != CVAR_STRING)
  987. {
  988. auto stringified = AZStd::to_string(value);
  989. if (!stringified.empty())
  990. {
  991. cvar->Set(stringified.c_str());
  992. }
  993. }
  994. }
  995. }
  996. return succeeded;
  997. }
  998. AZ::ConsoleCommandInvokedEvent::Handler ConsoleVariableEditor::m_commandInvokedHandler(
  999. [](AZStd::string_view command,
  1000. const AZ::ConsoleCommandContainer&,
  1001. AZ::ConsoleFunctorFlags,
  1002. AZ::ConsoleInvokedFrom)
  1003. {
  1004. if (command == AzToolsFramework::DocumentPropertyEditor::GetEnableCVarEditorName())
  1005. {
  1006. // the cvar editor pref changed, unregister the old and register the new
  1007. AzToolsFramework::UnregisterViewPane(LyViewPane::ConsoleVariables);
  1008. ConsoleVariableEditor::RegisterViewClass();
  1009. }
  1010. // find the cvar that changed and keep the console informed
  1011. auto changedCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(AZStd::string(command).c_str());
  1012. if (changedCVar)
  1013. {
  1014. auto console = AZ::Interface<AZ::IConsole>::Get();
  1015. auto azConsoleCommand = console->FindCommand(command);
  1016. if (azConsoleCommand)
  1017. {
  1018. const bool handled =
  1019. (SetCVarFromConsoleCommand<AZStd::string, CVAR_STRING>(changedCVar, azConsoleCommand) ||
  1020. SetCVarFromConsoleCommand<AZ::CVarFixedString, CVAR_STRING>(changedCVar, azConsoleCommand) ||
  1021. SetCVarFromConsoleCommand<AZ::s8, CVAR_INT>(changedCVar, azConsoleCommand) ||
  1022. SetCVarFromConsoleCommand<AZ::s16, CVAR_INT>(changedCVar, azConsoleCommand) ||
  1023. SetCVarFromConsoleCommand<AZ::s32, CVAR_INT>(changedCVar, azConsoleCommand) ||
  1024. SetCVarFromConsoleCommand<AZ::s64, CVAR_INT>(changedCVar, azConsoleCommand) ||
  1025. SetCVarFromConsoleCommand<AZ::u8, CVAR_INT>(changedCVar, azConsoleCommand) ||
  1026. SetCVarFromConsoleCommand<AZ::u16, CVAR_INT>(changedCVar, azConsoleCommand) ||
  1027. SetCVarFromConsoleCommand<AZ::u32, CVAR_INT>(changedCVar, azConsoleCommand) ||
  1028. SetCVarFromConsoleCommand<AZ::u64, CVAR_INT>(changedCVar, azConsoleCommand) ||
  1029. SetCVarFromConsoleCommand<bool, CVAR_INT>(changedCVar, azConsoleCommand) ||
  1030. SetCVarFromConsoleCommand<long, CVAR_INT>(changedCVar, azConsoleCommand) ||
  1031. SetCVarFromConsoleCommand<unsigned long, CVAR_INT>(changedCVar, azConsoleCommand) ||
  1032. SetCVarFromConsoleCommand<float, CVAR_FLOAT>(changedCVar, azConsoleCommand) ||
  1033. SetCVarFromConsoleCommand<double, CVAR_FLOAT>(changedCVar, azConsoleCommand)
  1034. );
  1035. if (!handled)
  1036. {
  1037. AZ_Warning("ConsoleSCB", false, "an unknown type could not be read into the console!");
  1038. }
  1039. }
  1040. if (!AzToolsFramework::DocumentPropertyEditor::ShouldReplaceCVarEditor())
  1041. {
  1042. OnVariableUpdated(changedCVar);
  1043. }
  1044. }
  1045. });
  1046. ConsoleVariableEditor::ConsoleVariableEditor(QWidget* parent)
  1047. : QWidget(parent)
  1048. , m_tableView(new QTableView(this))
  1049. , m_model(new ConsoleVariableModel(this))
  1050. , m_itemDelegate(new ConsoleVariableItemDelegate(this))
  1051. {
  1052. setWindowTitle(tr("Console Variables"));
  1053. // Setup our table view, don't show the actual headers
  1054. m_tableView->setEditTriggers(QAbstractItemView::SelectedClicked
  1055. | QAbstractItemView::DoubleClicked
  1056. | QAbstractItemView::EditKeyPressed
  1057. | QAbstractItemView::CurrentChanged);
  1058. m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
  1059. m_tableView->verticalHeader()->hide();
  1060. m_tableView->horizontalHeader()->hide();
  1061. // Setup a filter widget with a search label and line edit input for filtering
  1062. // the console variables
  1063. QWidget* m_filterWidget = new QWidget(this);
  1064. QLabel* label = new QLabel(tr("Search"), this);
  1065. QLineEdit* m_filterLineEdit = new QLineEdit(this);
  1066. QHBoxLayout* filterlayout = new QHBoxLayout(m_filterWidget);
  1067. filterlayout->addWidget(label);
  1068. filterlayout->addWidget(m_filterLineEdit);
  1069. // Setup our model to be filterable by the name column from our line edit
  1070. QSortFilterProxyModel* proxyModel = new QSortFilterProxyModel(this);
  1071. proxyModel->setSourceModel(m_model);
  1072. proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
  1073. proxyModel->setFilterKeyColumn(ColumnName);
  1074. m_tableView->setModel(proxyModel);
  1075. QObject::connect(m_filterLineEdit, &QLineEdit::textChanged, proxyModel, &QSortFilterProxyModel::setFilterWildcard);
  1076. // Set our value column to use our custom item delegate so we can inject
  1077. // our styled spin box for modifying the int/float values
  1078. m_tableView->setItemDelegateForColumn(ColumnValue, m_itemDelegate);
  1079. // Give our dialog a vertical layout with the search field on top and our
  1080. // table below it that will expand if the dialog is resized
  1081. QVBoxLayout* mainLayout = new QVBoxLayout(this);
  1082. mainLayout->setContentsMargins(0, 0, 0, 0);
  1083. mainLayout->addWidget(m_filterWidget);
  1084. mainLayout->addWidget(m_tableView, 1);
  1085. // Set the console variables
  1086. m_varBlock = VarBlockFromConsoleVars();
  1087. SetVarBlock(m_varBlock);
  1088. }
  1089. void ConsoleVariableEditor::SetVarBlock(CVarBlock* varBlock)
  1090. {
  1091. // Send our item delegate the new cvar block so it can use it to pull the
  1092. // limits for the selected variable
  1093. m_itemDelegate->SetVarBlock(varBlock);
  1094. // Update our model with the new cvar block
  1095. m_model->SetVarBlock(varBlock);
  1096. // Size our type column to be fit to the contents and allow our name and value
  1097. // columns to be stretched
  1098. m_tableView->resizeColumnToContents(ColumnType);
  1099. m_tableView->horizontalHeader()->setSectionResizeMode(ColumnName, QHeaderView::Stretch);
  1100. m_tableView->horizontalHeader()->setSectionResizeMode(ColumnValue, QHeaderView::Stretch);
  1101. // Select the first row by default after setting the model, or else the table
  1102. // view will just select the first cell on the first row by default which looks
  1103. // weird, and the other API calls like clear selection don't seem to affect it
  1104. m_tableView->selectRow(0);
  1105. }
  1106. void ConsoleVariableEditor::RegisterViewClass()
  1107. {
  1108. if (m_commandInvokedHandler.IsConnected())
  1109. {
  1110. m_commandInvokedHandler.Disconnect();
  1111. }
  1112. m_commandInvokedHandler.Connect(AZ::Interface<AZ::IConsole>::Get()->GetConsoleCommandInvokedEvent());
  1113. if (AzToolsFramework::DocumentPropertyEditor::ShouldReplaceCVarEditor())
  1114. {
  1115. AzToolsFramework::CvarDPE::RegisterViewClass();
  1116. }
  1117. else
  1118. {
  1119. AzToolsFramework::ViewPaneOptions opts;
  1120. opts.paneRect = QRect(100, 100, 340, 500);
  1121. AzToolsFramework::RegisterViewPane<ConsoleVariableEditor>(LyViewPane::ConsoleVariables, LyViewPane::CategoryOther, opts);
  1122. }
  1123. }
  1124. /**
  1125. * Update the IVariable in our var block when the corresponding ICVar has been
  1126. * changed
  1127. */
  1128. void ConsoleVariableEditor::HandleVariableRowUpdated(ICVar* pCVar)
  1129. {
  1130. const int varCount = m_varBlock->GetNumVariables();
  1131. for (int row = 0; row < varCount; ++row)
  1132. {
  1133. IVariable* var = m_varBlock->GetVariable(row);
  1134. if (var == nullptr)
  1135. {
  1136. continue;
  1137. }
  1138. if (var->GetName() == pCVar->GetName())
  1139. {
  1140. int varType = pCVar->GetType();
  1141. switch (varType)
  1142. {
  1143. case CVAR_INT:
  1144. var->Set(pCVar->GetIVal());
  1145. break;
  1146. case CVAR_FLOAT:
  1147. var->Set(pCVar->GetFVal());
  1148. break;
  1149. case CVAR_STRING:
  1150. var->Set(pCVar->GetString());
  1151. break;
  1152. }
  1153. // We need to let our model know that the underlying data has changed so
  1154. // that the view will be updated
  1155. QModelIndex index = m_model->index(row, ColumnValue);
  1156. Q_EMIT m_model->dataChanged(index, index);
  1157. return;
  1158. }
  1159. }
  1160. }
  1161. void ConsoleVariableEditor::showEvent(QShowEvent* event)
  1162. {
  1163. // Clear out our list of modified rows whenever our view is re-shown
  1164. m_model->ClearModifiedRows();
  1165. QWidget::showEvent(event);
  1166. }
  1167. void CConsoleSCB::showVariableEditor()
  1168. {
  1169. // Open the console variables pane
  1170. QtViewPaneManager::instance()->OpenPane(LyViewPane::ConsoleVariables);
  1171. }
  1172. void CConsoleSCB::toggleConsoleSearch()
  1173. {
  1174. if (!ui->findBar->isVisible())
  1175. {
  1176. ui->findBar->setVisible(true);
  1177. ui->lineEditFind->setFocus();
  1178. }
  1179. else
  1180. {
  1181. ui->findBar->setVisible(false);
  1182. }
  1183. }
  1184. void CConsoleSCB::findPrevious()
  1185. {
  1186. const auto text = ui->lineEditFind->text();
  1187. auto found = ui->textEdit->find(text, QTextDocument::FindBackward);
  1188. if (!found)
  1189. {
  1190. auto prevCursor = ui->textEdit->textCursor();
  1191. ui->textEdit->moveCursor(QTextCursor::End);
  1192. found = ui->textEdit->find(text, QTextDocument::FindBackward);
  1193. if (!found)
  1194. {
  1195. ui->textEdit->setTextCursor(prevCursor);
  1196. }
  1197. }
  1198. }
  1199. void CConsoleSCB::findNext()
  1200. {
  1201. const auto text = ui->lineEditFind->text();
  1202. auto found = ui->textEdit->find(text);
  1203. if (!found)
  1204. {
  1205. auto prevCursor = ui->textEdit->textCursor();
  1206. ui->textEdit->moveCursor(QTextCursor::Start);
  1207. found = ui->textEdit->find(text);
  1208. if (!found)
  1209. {
  1210. ui->textEdit->setTextCursor(prevCursor);
  1211. }
  1212. }
  1213. }
  1214. CConsoleSCB* CConsoleSCB::GetCreatedInstance()
  1215. {
  1216. return s_consoleSCB;
  1217. }
  1218. void CConsoleSCB::OnEditorNotifyEvent(EEditorNotifyEvent event)
  1219. {
  1220. switch (event)
  1221. {
  1222. case eNotify_OnBeginGameMode:
  1223. if (gSettings.clearConsoleOnGameModeStart)
  1224. {
  1225. ui->textEdit->clear();
  1226. }
  1227. break;
  1228. default:
  1229. break;
  1230. }
  1231. }
  1232. #include <Controls/moc_ConsoleSCB.cpp>