MainStatusBar.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  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 "MainStatusBar.h"
  10. #include <AzCore/Utils/Utils.h>
  11. // AzQtComponents
  12. #include <AzQtComponents/Components/Widgets/CheckBox.h>
  13. #include <AzQtComponents/Components/Style.h>
  14. #include <AzQtComponents/Utilities/DesktopUtilities.h>
  15. // Qt
  16. #include <QMenu>
  17. #include <QStyleOption>
  18. #include <QStylePainter>
  19. #include <QTimer>
  20. #include <QCheckBox>
  21. #include <QWidgetAction>
  22. // Editor
  23. #include "MainStatusBarItems.h"
  24. #include "ProcessInfo.h"
  25. const int iconTextSpacing = 3;
  26. const int marginSpacing = 2;
  27. const int spacerSpacing = 5;
  28. const int spacerColor = 0x6F6D6D;
  29. StatusBarItem::StatusBarItem(const QString& name, bool isClickable, MainStatusBar* parent, bool hasLeadingSpacer)
  30. : QWidget(parent)
  31. , m_isClickable(isClickable)
  32. , m_hasLeadingSpacer(hasLeadingSpacer)
  33. {
  34. setObjectName(name);
  35. }
  36. StatusBarItem::StatusBarItem(const QString& name, MainStatusBar* parent, bool hasLeadingSpacer)
  37. : StatusBarItem(name, false, parent, hasLeadingSpacer)
  38. {}
  39. void StatusBarItem::SetText(const QString& text)
  40. {
  41. if (text != m_text)
  42. {
  43. m_text = text;
  44. updateGeometry();
  45. update();
  46. }
  47. }
  48. void StatusBarItem::SetIcon(const QPixmap& icon)
  49. {
  50. QIcon origIcon = m_icon;
  51. if (icon.isNull())
  52. {
  53. m_icon = icon;
  54. }
  55. else
  56. {
  57. // avoid generating new pixmaps if we don't need to.
  58. if (icon.height() != 16)
  59. {
  60. m_icon = icon.scaledToHeight(16);
  61. }
  62. else
  63. {
  64. m_icon = icon;
  65. }
  66. }
  67. if (icon.isNull() ^ origIcon.isNull())
  68. {
  69. updateGeometry();
  70. }
  71. // don't generate paintevents unless we absolutely have changed!
  72. if (origIcon.cacheKey() != m_icon.cacheKey())
  73. {
  74. update();
  75. }
  76. }
  77. void StatusBarItem::SetIcon(const QIcon& icon)
  78. {
  79. QIcon origIcon = m_icon;
  80. m_icon = icon;
  81. if (icon.isNull() ^ origIcon.isNull())
  82. {
  83. updateGeometry();
  84. }
  85. // don't generate paintevents unless we absolutely have changed!
  86. if (origIcon.cacheKey() != m_icon.cacheKey())
  87. {
  88. update();
  89. }
  90. }
  91. void StatusBarItem::SetToolTip(const QString& tip)
  92. {
  93. setToolTip(tip);
  94. }
  95. void StatusBarItem::mousePressEvent(QMouseEvent* e)
  96. {
  97. if (m_isClickable && e->button() == Qt::LeftButton)
  98. {
  99. emit clicked();
  100. }
  101. }
  102. QSize StatusBarItem::sizeHint() const
  103. {
  104. QSize hint(4, 20);
  105. if (!m_icon.isNull())
  106. {
  107. hint.rwidth() += 16;
  108. }
  109. if (!m_icon.isNull() && !CurrentText().isEmpty())
  110. {
  111. hint.rwidth() += iconTextSpacing; //spacing
  112. }
  113. auto fm = fontMetrics();
  114. hint.rwidth() += fm.horizontalAdvance(CurrentText());
  115. hint.rwidth() += 2 * marginSpacing;
  116. hint.rheight() += 2 * marginSpacing;
  117. hint.rwidth() += m_hasLeadingSpacer ? spacerSpacing : 0;
  118. return hint;
  119. }
  120. QSize StatusBarItem::minimumSizeHint() const
  121. {
  122. return sizeHint();
  123. }
  124. void StatusBarItem::paintEvent([[maybe_unused]] QPaintEvent* pe)
  125. {
  126. QStylePainter painter(this);
  127. QStyleOption opt;
  128. opt.initFrom(this);
  129. painter.drawPrimitive(QStyle::PE_Widget, opt);
  130. auto rect = contentsRect();
  131. rect.adjust(marginSpacing, marginSpacing, -marginSpacing, -marginSpacing);
  132. QRect textRect = rect;
  133. if (m_hasLeadingSpacer)
  134. {
  135. textRect.adjust(spacerSpacing, 0, 0, 0);
  136. }
  137. if (!CurrentText().isEmpty())
  138. {
  139. painter.drawItemText(textRect, Qt::AlignLeft | Qt::AlignVCenter, this->palette(), true, CurrentText(), QPalette::WindowText);
  140. }
  141. if (!m_icon.isNull())
  142. {
  143. auto textWidth = textRect.width();
  144. if (textWidth > 0)
  145. {
  146. textWidth += iconTextSpacing; //margin
  147. }
  148. QRect iconRect = { textRect.left() + textWidth - textRect.height() - 1, textRect.top() + 2, textRect.height() - 4, textRect.height() - 4 };
  149. m_icon.paint(&painter, iconRect, Qt::AlignCenter);
  150. }
  151. if (m_hasLeadingSpacer)
  152. {
  153. QPen pen{ spacerColor };
  154. painter.setPen(pen);
  155. painter.drawLine(spacerSpacing / 2, 3, spacerSpacing / 2, rect.height() + 2);
  156. }
  157. }
  158. QString StatusBarItem::CurrentText() const
  159. {
  160. return m_text;
  161. }
  162. MainStatusBar* StatusBarItem::StatusBar() const
  163. {
  164. return static_cast<MainStatusBar*>(parentWidget());
  165. }
  166. ///////////////////////////////////////////////////////////////////////////////////////
  167. MainStatusBar::MainStatusBar(QWidget* parent)
  168. : QStatusBar(parent)
  169. {
  170. addPermanentWidget(new GeneralStatusItem(QStringLiteral("status"), this), 50);
  171. addPermanentWidget(new SourceControlItem(QStringLiteral("source_control"), this), 1);
  172. addPermanentWidget(new StatusBarItem(QStringLiteral("connection"), true, this, true), 1);
  173. addPermanentWidget(new GameInfoItem(QStringLiteral("game_info"), this), 1);
  174. addPermanentWidget(new MemoryStatusItem(QStringLiteral("memory"), this), 1);
  175. }
  176. void MainStatusBar::Init()
  177. {
  178. //called on mainwindow initialization
  179. const int statusbarTimerUpdateInterval{
  180. 500
  181. }; //in ms, so 2 FPS
  182. //ask for updates for items regularly. This is basically what MFC does
  183. auto timer = new QTimer(this);
  184. timer->setInterval(statusbarTimerUpdateInterval);
  185. connect(timer, &QTimer::timeout, this, &MainStatusBar::requestStatusUpdate);
  186. timer->start();
  187. }
  188. void MainStatusBar::SetStatusText(const QString& text)
  189. {
  190. SetItem(QStringLiteral("status"), text, QString(), QPixmap());
  191. }
  192. QWidget* MainStatusBar::SetItem(QString indicatorName, QString text, QString tip, const QPixmap& icon)
  193. {
  194. auto item = findChild<StatusBarItem*>(indicatorName, Qt::FindDirectChildrenOnly);
  195. assert(item);
  196. item->SetText(text);
  197. item->SetToolTip(tip);
  198. item->SetIcon(icon);
  199. return item;
  200. }
  201. QWidget* MainStatusBar::SetItem(QString indicatorName, QString text, QString tip, int iconId)
  202. {
  203. static std::unordered_map<int, QPixmap> idImages {
  204. {
  205. IDI_BALL_DISABLED, QPixmap(QStringLiteral(":/statusbar/res/ball_disabled.ico")).scaledToHeight(16)
  206. },
  207. {
  208. IDI_BALL_OFFLINE, QPixmap(QStringLiteral(":/statusbar/res/ball_offline.ico")).scaledToHeight(16)
  209. },
  210. {
  211. IDI_BALL_ONLINE, QPixmap(QStringLiteral(":/statusbar/res/ball_online.ico")).scaledToHeight(16)
  212. },
  213. {
  214. IDI_BALL_PENDING, QPixmap(QStringLiteral(":/statusbar/res/ball_pending.ico")).scaledToHeight(16)
  215. }
  216. };
  217. auto search = idImages.find(iconId);
  218. return SetItem(indicatorName, text, tip, search == idImages.end() ? QPixmap() : search->second);
  219. }
  220. QWidget* MainStatusBar::GetItem(QString indicatorName)
  221. {
  222. return findChild<QWidget*>(indicatorName);
  223. }
  224. ////////////////////////////////////////////////////////////////////////////////////////////////////////////
  225. SourceControlItem::SourceControlItem(QString name, MainStatusBar* parent)
  226. : StatusBarItem(name, true, parent)
  227. , m_sourceControlAvailable(false)
  228. {
  229. if (AzToolsFramework::SourceControlConnectionRequestBus::HasHandlers())
  230. {
  231. AzToolsFramework::SourceControlNotificationBus::Handler::BusConnect();
  232. m_sourceControlAvailable = true;
  233. }
  234. InitMenu();
  235. connect(this, &StatusBarItem::clicked, this, &SourceControlItem::UpdateAndShowMenu);
  236. }
  237. SourceControlItem::~SourceControlItem()
  238. {
  239. AzToolsFramework::SourceControlNotificationBus::Handler::BusDisconnect();
  240. }
  241. void SourceControlItem::UpdateAndShowMenu()
  242. {
  243. if (m_sourceControlAvailable)
  244. {
  245. UpdateMenuItems();
  246. m_menu->popup(QCursor::pos());
  247. }
  248. }
  249. void SourceControlItem::ConnectivityStateChanged(const AzToolsFramework::SourceControlState state)
  250. {
  251. m_SourceControlState = state;
  252. UpdateMenuItems();
  253. }
  254. void SourceControlItem::InitMenu()
  255. {
  256. m_scIconOk = QIcon(":statusbar/res/source_control_connected.svg");
  257. m_scIconError = QIcon(":statusbar/res/source_control_error_v2.svg");
  258. m_scIconWarning = QIcon(":statusbar/res/source_control-warning_v2.svg");
  259. m_scIconDisabled = QIcon(":statusbar/res/source_control-not_setup.svg");
  260. if (m_sourceControlAvailable)
  261. {
  262. m_menu = std::make_unique<QMenu>();
  263. m_settingsAction = m_menu->addAction(tr("Settings"));
  264. m_checkBox = new QCheckBox(m_menu.get());
  265. m_checkBox->setText(tr("Enable"));
  266. AzQtComponents::CheckBox::applyToggleSwitchStyle(m_checkBox);
  267. m_enableAction = new QWidgetAction(m_menu.get());
  268. m_enableAction->setDefaultWidget(m_checkBox);
  269. m_menu->addAction(m_settingsAction);
  270. m_menu->addAction(m_enableAction);
  271. AzQtComponents::Style::addClass(m_menu.get(), "SourceControlMenu");
  272. m_enableAction->setCheckable(true);
  273. m_enableAction->setEnabled(true);
  274. {
  275. using namespace AzToolsFramework;
  276. m_SourceControlState = SourceControlState::Disabled;
  277. SourceControlConnectionRequestBus::BroadcastResult(m_SourceControlState, &SourceControlConnectionRequestBus::Events::GetSourceControlState);
  278. UpdateMenuItems();
  279. }
  280. connect(m_settingsAction, &QAction::triggered, this, [&]()
  281. {
  282. GetIEditor()->GetSourceControl()->ShowSettings();
  283. });
  284. connect(m_checkBox, &QCheckBox::stateChanged, this, [this](int state) {SetSourceControlEnabledState(state); });
  285. }
  286. else
  287. {
  288. SetIcon(m_scIconDisabled);
  289. SetToolTip(tr("No source control provided"));
  290. }
  291. SetText("P4V");
  292. }
  293. void SourceControlItem::SetSourceControlEnabledState(bool state)
  294. {
  295. using SCRequest = AzToolsFramework::SourceControlConnectionRequestBus;
  296. SCRequest::Broadcast(&SCRequest::Events::EnableSourceControl, state);
  297. m_menu->hide();
  298. }
  299. void SourceControlItem::UpdateMenuItems()
  300. {
  301. QString toolTip;
  302. bool disabled = false;
  303. bool errorIcon = false;
  304. switch (m_SourceControlState)
  305. {
  306. case AzToolsFramework::SourceControlState::Disabled:
  307. toolTip = tr("Perforce disabled");
  308. disabled = true;
  309. errorIcon = true;
  310. break;
  311. case AzToolsFramework::SourceControlState::ConfigurationInvalid:
  312. errorIcon = true;
  313. toolTip = tr("Perforce configuration invalid");
  314. break;
  315. case AzToolsFramework::SourceControlState::Active:
  316. toolTip = tr("Perforce connected");
  317. break;
  318. }
  319. m_settingsAction->setEnabled(!disabled);
  320. m_checkBox->setChecked(!disabled);
  321. m_checkBox->setText(disabled ? tr("Status: Offline") : tr("Status: Online"));
  322. SetIcon(errorIcon ? disabled ? m_scIconDisabled : m_scIconWarning : m_scIconOk);
  323. SetToolTip(toolTip);
  324. }
  325. ////////////////////////////////////////////////////////////////////////////////////////////////////////////
  326. MemoryStatusItem::MemoryStatusItem(QString name, MainStatusBar* parent)
  327. : StatusBarItem(name, parent)
  328. {
  329. connect(parent, &MainStatusBar::requestStatusUpdate, this, &MemoryStatusItem::updateStatus);
  330. SetToolTip(tr("Memory usage"));
  331. }
  332. MemoryStatusItem::~MemoryStatusItem()
  333. {
  334. }
  335. void MemoryStatusItem::updateStatus()
  336. {
  337. AZ::ProcessMemInfo mi;
  338. AZ::QueryMemInfo(mi);
  339. uint64 nSizeMb = (uint64)(mi.m_workingSet / (1024 * 1024));
  340. SetText(QString("%1 Mb").arg(nSizeMb));
  341. }
  342. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  343. GeneralStatusItem::GeneralStatusItem(QString name, MainStatusBar* parent)
  344. : StatusBarItem(name, parent)
  345. {
  346. connect(parent, &MainStatusBar::messageChanged, this, [this](const QString&) { update(); });
  347. }
  348. QString GeneralStatusItem::CurrentText() const
  349. {
  350. if (!StatusBar()->currentMessage().isEmpty())
  351. {
  352. return StatusBar()->currentMessage();
  353. }
  354. return StatusBarItem::CurrentText();
  355. }
  356. GameInfoItem::GameInfoItem(QString name, MainStatusBar* parent)
  357. : StatusBarItem(name, parent, true)
  358. {
  359. m_projectPath = QString::fromUtf8(AZ::Utils::GetProjectPath().c_str());
  360. SetText(QObject::tr("GameFolder: '%1'").arg(m_projectPath));
  361. SetToolTip(QObject::tr("Game Info"));
  362. setContextMenuPolicy(Qt::CustomContextMenu);
  363. QObject::connect(this, &QWidget::customContextMenuRequested, this, &GameInfoItem::OnShowContextMenu);
  364. }
  365. void GameInfoItem::OnShowContextMenu(const QPoint& pos)
  366. {
  367. QMenu contextMenu(this);
  368. // Context menu action to open the project folder in file browser
  369. contextMenu.addAction(AzQtComponents::fileBrowserActionName(), this, [this]() {
  370. AzQtComponents::ShowFileOnDesktop(m_projectPath);
  371. });
  372. contextMenu.exec(mapToGlobal(pos));
  373. }
  374. #include <moc_MainStatusBar.cpp>
  375. #include <moc_MainStatusBarItems.cpp>