WelcomeScreenDialog.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  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 "WelcomeScreenDialog.h"
  10. // Qt
  11. #include <QTableWidget>
  12. #include <QTableWidgetItem>
  13. #include <QToolTip>
  14. #include <QMenu>
  15. #include <QDesktopServices>
  16. #include <QFileDialog>
  17. #include <QMessageBox>
  18. #include <QScreen>
  19. #include <QDesktopWidget>
  20. #include <QTimer>
  21. #include <QDateTime>
  22. #include <AzCore/Utils/Utils.h>
  23. // AzToolsFramework
  24. #include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
  25. // AzQtComponents
  26. #include <AzQtComponents/Components/Widgets/CheckBox.h>
  27. #include <AzQtComponents/Components/WindowDecorationWrapper.h>
  28. #include <AzQtComponents/Utilities/PixmapScaleUtilities.h>
  29. // Editor
  30. #include "Settings.h"
  31. #include "MainWindow.h"
  32. #include "CryEdit.h"
  33. #include "LevelFileDialog.h"
  34. AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
  35. #include <WelcomeScreen/ui_WelcomeScreenDialog.h>
  36. AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
  37. using namespace AzQtComponents;
  38. #define WMSEVENTNAME "WMSEvent"
  39. #define WMSEVENTOPERATION "operation"
  40. static int GetSmallestScreenHeight()
  41. {
  42. int smallestHeight = -1;
  43. for (QScreen* screen : QApplication::screens())
  44. {
  45. int screenHeight = screen->availableGeometry().height();
  46. if ((smallestHeight < 0) || (smallestHeight > screenHeight))
  47. {
  48. smallestHeight = screenHeight;
  49. }
  50. }
  51. return smallestHeight;
  52. }
  53. WelcomeScreenDialog::WelcomeScreenDialog(QWidget* pParent)
  54. : QDialog(new WindowDecorationWrapper(WindowDecorationWrapper::OptionAutoAttach | WindowDecorationWrapper::OptionAutoTitleBarButtons, pParent), Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint | Qt::WindowTitleHint)
  55. , ui(new Ui::WelcomeScreenDialog)
  56. , m_pRecentList(nullptr)
  57. {
  58. ui->setupUi(this);
  59. // Set the project preview image
  60. QString projectPreviewPath = QDir(AZ::Utils::GetProjectPath().c_str()).filePath("preview.png");
  61. QFileInfo projectPreviewPathInfo(projectPreviewPath);
  62. if (!projectPreviewPathInfo.exists() || !projectPreviewPathInfo.isFile())
  63. {
  64. projectPreviewPath = ":/WelcomeScreenDialog/DefaultProjectImage.png";
  65. }
  66. ui->activeProjectIcon->setPixmap(
  67. AzQtComponents::ScalePixmapForScreenDpi(
  68. QPixmap(projectPreviewPath),
  69. screen(),
  70. ui->activeProjectIcon->size(),
  71. Qt::KeepAspectRatioByExpanding,
  72. Qt::SmoothTransformation
  73. )
  74. );
  75. ui->recentLevelTable->setColumnCount(3);
  76. ui->recentLevelTable->setMouseTracking(true);
  77. ui->recentLevelTable->setContextMenuPolicy(Qt::CustomContextMenu);
  78. ui->recentLevelTable->horizontalHeader()->hide();
  79. ui->recentLevelTable->verticalHeader()->hide();
  80. ui->recentLevelTable->setSelectionBehavior(QAbstractItemView::SelectRows);
  81. ui->recentLevelTable->setSelectionMode(QAbstractItemView::SingleSelection);
  82. ui->recentLevelTable->setIconSize(QSize(20, 20));
  83. installEventFilter(this);
  84. auto projectDisplayName = AZ::Utils::GetProjectDisplayName();
  85. ui->currentProjectName->setText(projectDisplayName.c_str());
  86. ui->newLevelButton->setDefault(true);
  87. // Hide these buttons until the new functionality is added
  88. ui->gridButton->hide();
  89. ui->objectListButton->hide();
  90. ui->switchProjectButton->hide();
  91. connect(ui->recentLevelTable, &QWidget::customContextMenuRequested, this, &WelcomeScreenDialog::OnShowContextMenu);
  92. connect(ui->recentLevelTable, &QTableWidget::entered, this, &WelcomeScreenDialog::OnShowToolTip);
  93. connect(ui->recentLevelTable, &QTableWidget::clicked, this, &WelcomeScreenDialog::OnRecentLevelTableItemClicked);
  94. connect(ui->newLevelButton, &QPushButton::clicked, this, &WelcomeScreenDialog::OnNewLevelBtnClicked);
  95. connect(ui->levelFileLabel, &QLabel::linkActivated, this, &WelcomeScreenDialog::OnNewLevelLabelClicked);
  96. connect(ui->openLevelButton, &QPushButton::clicked, this, &WelcomeScreenDialog::OnOpenLevelBtnClicked);
  97. // Adjust the height, if need be
  98. // Do it in the constructor so that the WindowDecoratorWrapper handles it correctly
  99. int smallestHeight = GetSmallestScreenHeight();
  100. if (smallestHeight < geometry().height())
  101. {
  102. const int SOME_PADDING_IN_PIXELS = 90;
  103. int difference = geometry().height() - (smallestHeight - SOME_PADDING_IN_PIXELS);
  104. QRect newGeometry = geometry().adjusted(0, difference / 2, 0, -difference / 2);
  105. setMinimumSize(minimumSize().width(), newGeometry.height());
  106. resize(newGeometry.size());
  107. }
  108. m_levelExtension = EditorUtils::LevelFile::GetDefaultFileExtension();
  109. }
  110. WelcomeScreenDialog::~WelcomeScreenDialog()
  111. {
  112. delete ui;
  113. }
  114. void WelcomeScreenDialog::done(int result)
  115. {
  116. QDialog::done(result);
  117. }
  118. const QString& WelcomeScreenDialog::GetLevelPath()
  119. {
  120. return m_levelPath;
  121. }
  122. bool WelcomeScreenDialog::eventFilter(QObject *watched, QEvent *event)
  123. {
  124. if (event->type() == QEvent::Show)
  125. {
  126. ui->recentLevelTable->horizontalHeader()->resizeSection(0, ui->nameLabel->width());
  127. ui->recentLevelTable->horizontalHeader()->resizeSection(1, ui->modifiedLabel->width());
  128. ui->recentLevelTable->horizontalHeader()->resizeSection(2, ui->typeLabel->width());
  129. }
  130. return QDialog::eventFilter(watched, event);
  131. }
  132. bool WelcomeScreenDialog::IsValidLevelName(const QString& path)
  133. {
  134. QStringList pathParts = Path::SplitIntoSegments(path);
  135. QString levelName = pathParts.at(pathParts.size() - 1);
  136. if (levelName.endsWith(".prefab", Qt::CaseInsensitive))
  137. {
  138. // If the level is a prefab, check the container name.
  139. levelName = pathParts.at(pathParts.size() - 2);
  140. }
  141. QRegExpValidator validator(QRegExp("^[a-zA-Z0-9_\\-./]*$"));
  142. int pos = 0;
  143. return validator.validate(levelName, pos);
  144. }
  145. void WelcomeScreenDialog::SetRecentFileList(RecentFileList* pList)
  146. {
  147. if (!pList)
  148. {
  149. return;
  150. }
  151. m_pRecentList = pList;
  152. auto projectPath = AZ::Utils::GetProjectPath();
  153. QString gamePath{projectPath.c_str()};
  154. Path::ConvertSlashToBackSlash(gamePath);
  155. gamePath = Path::ToUnixPath(gamePath.toLower());
  156. gamePath = Path::AddSlash(gamePath);
  157. QString sCurDir = (Path::GetEditingGameDataFolder() + QDir::separator().toLatin1()).c_str();
  158. int nCurDir = sCurDir.length();
  159. int recentListSize = pList->GetSize();
  160. int currentRow = 0;
  161. ui->recentLevelTable->setRowCount(recentListSize);
  162. for (int i = 0; i < recentListSize; ++i)
  163. {
  164. const QString& recentFile = pList->m_arrNames[i];
  165. if (recentFile.endsWith(m_levelExtension) && IsValidLevelName(recentFile))
  166. {
  167. if (CFileUtil::Exists(recentFile, false))
  168. {
  169. QString sCurEntryDir = recentFile.left(nCurDir);
  170. if (sCurEntryDir.compare(sCurDir, Qt::CaseInsensitive) == 0)
  171. {
  172. QString fullPath = recentFile;
  173. const QString name = Path::GetFile(fullPath);
  174. Path::ConvertSlashToBackSlash(fullPath);
  175. fullPath = Path::ToUnixPath(fullPath.toLower());
  176. fullPath = Path::AddSlash(fullPath);
  177. if (fullPath.contains(gamePath))
  178. {
  179. if (gSettings.prefabSystem)
  180. {
  181. QIcon icon;
  182. icon.addFile(QString::fromUtf8(":/Level/level.svg"), QSize(), QIcon::Normal, QIcon::Off);
  183. ui->recentLevelTable->setItem(currentRow, 0, new QTableWidgetItem(icon, name));
  184. }
  185. else
  186. {
  187. ui->recentLevelTable->setItem(currentRow, 0, new QTableWidgetItem(name));
  188. }
  189. QFileInfo file(recentFile);
  190. QDateTime dateTime = file.lastModified();
  191. QString date = QLocale::system().toString(dateTime.date(), QLocale::ShortFormat) + " " +
  192. QLocale::system().toString(dateTime.time(), QLocale::LongFormat);
  193. ui->recentLevelTable->setItem(currentRow, 1, new QTableWidgetItem(date));
  194. ui->recentLevelTable->setItem(currentRow++, 2, new QTableWidgetItem(tr("Level")));
  195. m_levels.push_back(std::make_pair(name, recentFile));
  196. }
  197. }
  198. }
  199. }
  200. }
  201. ui->recentLevelTable->setRowCount(currentRow);
  202. ui->recentLevelTable->setMinimumHeight(currentRow * ui->recentLevelTable->verticalHeader()->defaultSectionSize());
  203. ui->recentLevelTable->setMaximumHeight(currentRow * ui->recentLevelTable->verticalHeader()->defaultSectionSize());
  204. ui->levelFileLabel->setVisible(currentRow ? false : true);
  205. ui->recentLevelTable->setCurrentIndex(QModelIndex());
  206. }
  207. void WelcomeScreenDialog::RemoveLevelEntry(int index)
  208. {
  209. TNamePathPair levelPath = m_levels[index];
  210. ui->recentLevelTable->removeRow(index);
  211. m_levels.erase(m_levels.begin() + index);
  212. if (!m_pRecentList)
  213. {
  214. return;
  215. }
  216. for (int i = 0; i < m_pRecentList->GetSize(); ++i)
  217. {
  218. QString fullPath = m_pRecentList->m_arrNames[i];
  219. QString fullPath2 = levelPath.second;
  220. // path from recent list
  221. Path::ConvertSlashToBackSlash(fullPath);
  222. fullPath = Path::ToUnixPath(fullPath.toLower());
  223. fullPath = Path::AddPathSlash(fullPath);
  224. // path from our dashboard list
  225. Path::ConvertSlashToBackSlash(fullPath2);
  226. fullPath2 = Path::ToUnixPath(fullPath2.toLower());
  227. fullPath2 = Path::AddPathSlash(fullPath2);
  228. if (fullPath == fullPath2)
  229. {
  230. m_pRecentList->Remove(index);
  231. break;
  232. }
  233. }
  234. m_pRecentList->WriteList();
  235. }
  236. void WelcomeScreenDialog::OnShowToolTip(const QModelIndex& index)
  237. {
  238. const QString& fullPath = m_levels[index.row()].second;
  239. QToolTip::showText(QCursor::pos(), QString("Open level: %1").arg(fullPath));
  240. }
  241. void WelcomeScreenDialog::OnShowContextMenu(const QPoint& pos)
  242. {
  243. QModelIndex index = ui->recentLevelTable->indexAt(pos);
  244. if (index.isValid())
  245. {
  246. QString level = ui->recentLevelTable->itemAt(pos)->text();
  247. QPoint globalPos = ui->recentLevelTable->viewport()->mapToGlobal(pos);
  248. QMenu contextMenu;
  249. contextMenu.addAction(QString("Remove " + level + " from recent list"));
  250. QAction* selectedItem = contextMenu.exec(globalPos);
  251. if (selectedItem)
  252. {
  253. RemoveLevelEntry(index.row());
  254. }
  255. }
  256. }
  257. void WelcomeScreenDialog::OnNewLevelBtnClicked([[maybe_unused]] bool checked)
  258. {
  259. m_levelPath = "new";
  260. accept();
  261. }
  262. void WelcomeScreenDialog::OnNewLevelLabelClicked(const QString& path)
  263. {
  264. if (path == "Create")
  265. {
  266. OnNewLevelBtnClicked(true);
  267. }
  268. else
  269. {
  270. OnOpenLevelBtnClicked(true);
  271. }
  272. }
  273. void WelcomeScreenDialog::OnOpenLevelBtnClicked([[maybe_unused]] bool checked)
  274. {
  275. CLevelFileDialog dlg(true, this);
  276. if (dlg.exec() == QDialog::Accepted)
  277. {
  278. m_levelPath = dlg.GetFileName();
  279. accept();
  280. }
  281. }
  282. void WelcomeScreenDialog::OnRecentLevelTableItemClicked(const QModelIndex& modelIndex)
  283. {
  284. int index = modelIndex.row();
  285. if (index >= 0 && index < m_levels.size())
  286. {
  287. m_levelPath = m_levels[index].second;
  288. accept();
  289. }
  290. }
  291. void WelcomeScreenDialog::OnCloseBtnClicked([[maybe_unused]] bool checked)
  292. {
  293. accept();
  294. }
  295. void WelcomeScreenDialog::previewAreaScrolled()
  296. {
  297. //this should only be reported once per session
  298. if (m_messageScrollReported)
  299. {
  300. return;
  301. }
  302. m_messageScrollReported = true;
  303. }
  304. #include <WelcomeScreen/moc_WelcomeScreenDialog.cpp>