EditorMenu.cpp 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255
  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 "EditorCommon.h"
  9. #include <AzQtComponents/Buses/ShortcutDispatch.h>
  10. #include <AzToolsFramework/Slice/SliceUtilities.h>
  11. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  12. #include "AlignToolbarSection.h"
  13. #include "ViewportAlign.h"
  14. #include "CanvasHelpers.h"
  15. #include "GuideHelpers.h"
  16. #include <LyShine/Bus/UiEditorCanvasBus.h>
  17. #include <Util/PathUtil.h>
  18. #include <QFileDialog>
  19. #include <QMenuBar>
  20. #include <QUndoGroup>
  21. #include <QUndoView>
  22. #include <QDesktopServices>
  23. #include <QMessageBox>
  24. #include <QDockWidget>
  25. static const bool debugViewUndoStack = false;
  26. QAction* EditorWindow::AddMenuAction(const QString& text, bool enabled, QMenu* menu, AZStd::function<void (bool)> function)
  27. {
  28. QAction* action = new QAction(text, this);
  29. action->setEnabled(enabled);
  30. QObject::connect(action,
  31. &QAction::triggered,
  32. function);
  33. menu->addAction(action);
  34. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  35. m_actionsEnabledWithSelection.push_back(action);
  36. return action;
  37. }
  38. void EditorWindow::EditorMenu_Open(QString optional_selectedFile)
  39. {
  40. if (optional_selectedFile.isEmpty())
  41. {
  42. QString dir;
  43. QStringList recentFiles = ReadRecentFiles();
  44. // If we had recently opened canvases, open the most recent one's directory
  45. if (recentFiles.size() > 0)
  46. {
  47. dir = Path::GetPath(recentFiles.front());
  48. }
  49. // Else go to the default canvas directory
  50. else
  51. {
  52. dir = FileHelpers::GetAbsoluteDir(UICANVASEDITOR_CANVAS_DIRECTORY);
  53. }
  54. QFileDialog dialog(this, QString(), dir, "*." UICANVASEDITOR_CANVAS_EXTENSION);
  55. dialog.setFileMode(QFileDialog::ExistingFiles);
  56. if (dialog.exec() == QDialog::Accepted)
  57. {
  58. OpenCanvases(dialog.selectedFiles());
  59. }
  60. }
  61. else
  62. {
  63. OpenCanvas(optional_selectedFile);
  64. }
  65. }
  66. void EditorWindow::AddMenu_File()
  67. {
  68. QMenu* menu = menuBar()->addMenu("&File");
  69. menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
  70. // Create a new canvas.
  71. {
  72. QAction* action = new QAction("&New Canvas", this);
  73. action->setShortcut(QKeySequence::New);
  74. QObject::connect(action,
  75. &QAction::triggered,
  76. this,
  77. &EditorWindow::NewCanvas);
  78. menu->addAction(action);
  79. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  80. }
  81. // Load a canvas.
  82. {
  83. QAction* action = new QAction("&Open Canvas...", this);
  84. action->setShortcut(QKeySequence::Open);
  85. QObject::connect(action,
  86. &QAction::triggered,
  87. this,
  88. [ this ]([[maybe_unused]] bool checked)
  89. {
  90. EditorMenu_Open("");
  91. });
  92. menu->addAction(action);
  93. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  94. }
  95. bool canvasLoaded = GetCanvas().IsValid();
  96. menu->addSeparator();
  97. UiCanvasMetadata *canvasMetadata = canvasLoaded ? GetCanvasMetadata(GetCanvas()) : nullptr;
  98. if (canvasMetadata && canvasMetadata->m_isSliceEditing)
  99. {
  100. // Save the slice
  101. {
  102. QAction *action = CreateSaveSliceAction(canvasMetadata);
  103. menu->addAction(action);
  104. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  105. }
  106. }
  107. else
  108. {
  109. // Save the canvas
  110. {
  111. QAction *action = CreateSaveCanvasAction(GetCanvas());
  112. menu->addAction(action);
  113. addAction(action);
  114. }
  115. // Save the canvas with new file name
  116. {
  117. QAction* action = CreateSaveCanvasAsAction(GetCanvas());
  118. menu->addAction(action);
  119. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  120. }
  121. }
  122. // Save all the canvases
  123. {
  124. QAction* action = CreateSaveAllCanvasesAction();
  125. menu->addAction(action);
  126. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  127. }
  128. menu->addSeparator();
  129. // Close the active canvas
  130. {
  131. QAction* action = CreateCloseCanvasAction(GetCanvas());
  132. menu->addAction(action);
  133. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  134. }
  135. // Close all canvases
  136. {
  137. QAction* action = CreateCloseAllCanvasesAction();
  138. menu->addAction(action);
  139. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  140. }
  141. // Close all but the active canvas
  142. {
  143. QAction* action = CreateCloseAllOtherCanvasesAction(GetCanvas());
  144. menu->addAction(action);
  145. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  146. }
  147. menu->addSeparator();
  148. // Recent Files.
  149. {
  150. QStringList recentFiles = ReadRecentFiles();
  151. // List of recent files.
  152. {
  153. QMenu* recentMenu = menu->addMenu("&Recent Files");
  154. recentMenu->setEnabled(!recentFiles.isEmpty());
  155. // QStringList -> QMenu.
  156. for (auto && fileName : recentFiles)
  157. {
  158. QAction* action = new QAction(fileName, this);
  159. QObject::connect(action,
  160. &QAction::triggered,
  161. this,
  162. [ this, fileName ]([[maybe_unused]] bool checked)
  163. {
  164. EditorMenu_Open(fileName);
  165. });
  166. recentMenu->addAction(action);
  167. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  168. }
  169. }
  170. // Clear Recent Files.
  171. {
  172. QAction* action = new QAction("Clear Recent Files", this);
  173. action->setEnabled(!recentFiles.isEmpty());
  174. QObject::connect(action,
  175. &QAction::triggered,
  176. this,
  177. [ this ]([[maybe_unused]] bool checked)
  178. {
  179. ClearRecentFile();
  180. RefreshEditorMenu();
  181. });
  182. menu->addAction(action);
  183. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  184. }
  185. }
  186. }
  187. void EditorWindow::AddMenuItems_Edit(QMenu* menu)
  188. {
  189. // Undo.
  190. {
  191. QAction* action = GetUndoGroup()->createUndoAction(this);
  192. action->setShortcut(QKeySequence::Undo);
  193. menu->addAction(action);
  194. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  195. }
  196. // Redo.
  197. {
  198. QAction* action = GetUndoGroup()->createRedoAction(this);
  199. // IMPORTANT: We CAN'T just provide QKeySequence::Redo as a
  200. // shortcut because the menu will show CTRL+Y as the shortcut.
  201. // To display CTRL+SHIFT+Z by default, we have to provide the
  202. // list of shortcuts explicitly.
  203. {
  204. action->setShortcuts(QList<QKeySequence>{AzQtComponents::RedoKeySequence,
  205. QKeySequence(Qt::META + Qt::SHIFT + Qt::Key_Z),
  206. QKeySequence(QKeySequence::Redo)});
  207. }
  208. menu->addAction(action);
  209. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  210. }
  211. bool canvasLoaded = GetCanvas().IsValid();
  212. menu->addSeparator();
  213. // Select All.
  214. {
  215. QAction* action = new QAction("Select &All", this);
  216. action->setShortcut(QKeySequence::SelectAll);
  217. action->setEnabled(canvasLoaded);
  218. QObject::connect(action,
  219. &QAction::triggered,
  220. GetHierarchy(),
  221. &HierarchyWidget::selectAll);
  222. menu->addAction(action);
  223. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  224. }
  225. menu->addSeparator();
  226. bool itemsAreSelected = !GetHierarchy()->selectedItems().isEmpty();
  227. bool thereIsContentInTheClipboard = ClipboardContainsOurDataType();
  228. // Cut.
  229. {
  230. QAction* action = new QAction("Cu&t", this);
  231. action->setShortcut(QKeySequence::Cut);
  232. action->setEnabled(itemsAreSelected);
  233. QObject::connect(action,
  234. &QAction::triggered,
  235. GetHierarchy(),
  236. &HierarchyWidget::Cut);
  237. menu->addAction(action);
  238. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  239. m_actionsEnabledWithSelection.push_back(action);
  240. }
  241. // Copy.
  242. {
  243. QAction* action = new QAction("&Copy", this);
  244. action->setShortcut(QKeySequence::Copy);
  245. action->setEnabled(itemsAreSelected);
  246. QObject::connect(action,
  247. &QAction::triggered,
  248. GetHierarchy(),
  249. &HierarchyWidget::Copy);
  250. menu->addAction(action);
  251. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  252. m_actionsEnabledWithSelection.push_back(action);
  253. }
  254. // Paste.
  255. {
  256. // Paste as silbing.
  257. {
  258. QAction* action = new QAction(itemsAreSelected ? "&Paste as sibling" : "&Paste", this);
  259. action->setShortcut(QKeySequence::Paste);
  260. action->setEnabled(canvasLoaded && thereIsContentInTheClipboard);
  261. QObject::connect(action,
  262. &QAction::triggered,
  263. GetHierarchy(),
  264. &HierarchyWidget::PasteAsSibling);
  265. menu->addAction(action);
  266. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  267. m_pasteAsSiblingAction = action;
  268. }
  269. // Paste as child.
  270. {
  271. QAction* action = new QAction("Paste as c&hild", this);
  272. {
  273. action->setShortcuts(QList<QKeySequence>{QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_V),
  274. QKeySequence(Qt::META + Qt::SHIFT + Qt::Key_V)});
  275. }
  276. action->setEnabled(canvasLoaded && thereIsContentInTheClipboard && itemsAreSelected);
  277. QObject::connect(action,
  278. &QAction::triggered,
  279. GetHierarchy(),
  280. &HierarchyWidget::PasteAsChild);
  281. menu->addAction(action);
  282. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  283. m_pasteAsChildAction = action;
  284. }
  285. }
  286. if (debugViewUndoStack)
  287. {
  288. QAction* action = new QAction("[DEBUG] View undo stack", this);
  289. action->setEnabled(canvasLoaded);
  290. QObject::connect(action,
  291. &QAction::triggered,
  292. this,
  293. [this]([[maybe_unused]] bool checked)
  294. {
  295. static QUndoView* undoView = nullptr;
  296. if (undoView)
  297. {
  298. undoView->setGroup(GetUndoGroup());
  299. }
  300. else
  301. {
  302. undoView = new QUndoView(GetUndoGroup());
  303. undoView->setWindowTitle("[DEBUG] Undo stack");
  304. undoView->setAttribute(Qt::WA_QuitOnClose, false);
  305. }
  306. undoView->show();
  307. });
  308. menu->addAction(action);
  309. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  310. }
  311. menu->addSeparator();
  312. // Find elements
  313. {
  314. QAction* action = new QAction("&Find Elements...", this);
  315. action->setShortcut(QKeySequence::Find);
  316. action->setEnabled(canvasLoaded);
  317. QObject::connect(action,
  318. &QAction::triggered,
  319. [this]([[maybe_unused]] bool checked)
  320. {
  321. ShowEntitySearchModal();
  322. });
  323. menu->addAction(action);
  324. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  325. }
  326. menu->addSeparator();
  327. // Delete.
  328. {
  329. QAction* action = new QAction("Delete", this);
  330. action->setShortcut(QKeySequence::Delete);
  331. action->setEnabled(itemsAreSelected);
  332. QObject::connect(action,
  333. &QAction::triggered,
  334. GetHierarchy(),
  335. [this]() { GetHierarchy()->DeleteSelectedItems(); });
  336. menu->addAction(action);
  337. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  338. m_actionsEnabledWithSelection.push_back(action);
  339. }
  340. // Add Align sub-menu
  341. {
  342. QMenu* alignMenu = menu->addMenu("Align");
  343. auto viewport = canvasLoaded ? GetViewport() : nullptr;
  344. bool enabled = viewport && itemsAreSelected && ViewportAlign::IsAlignAllowed(this);
  345. // Add each sub-menu item. Store the QActions so that we can enable/disable them when align is allowed or not.
  346. {
  347. m_actionsEnabledWithAlignAllowed.push_back(
  348. AddMenuAction("Top Edges", enabled, alignMenu,
  349. [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::VerticalTop); } ));
  350. m_actionsEnabledWithAlignAllowed.push_back(
  351. AddMenuAction("Vertical Centers", enabled, alignMenu,
  352. [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::VerticalCenter); } ));
  353. m_actionsEnabledWithAlignAllowed.push_back(
  354. AddMenuAction("Bottom Edges", enabled, alignMenu,
  355. [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::VerticalBottom); } ));
  356. m_actionsEnabledWithAlignAllowed.push_back(
  357. AddMenuAction("Left Edges", enabled, alignMenu,
  358. [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::HorizontalLeft); } ));
  359. m_actionsEnabledWithAlignAllowed.push_back(
  360. AddMenuAction("Horizontal Centers", enabled, alignMenu,
  361. [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::HorizontalCenter); } ));
  362. m_actionsEnabledWithAlignAllowed.push_back(
  363. AddMenuAction("Right Edges", enabled, alignMenu,
  364. [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::HorizontalRight); } ));
  365. }
  366. }
  367. }
  368. void EditorWindow::AddMenu_Edit()
  369. {
  370. QMenu* menu = menuBar()->addMenu("&Edit");
  371. menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
  372. AddMenuItems_Edit(menu);
  373. }
  374. void EditorWindow::AddMenu_View()
  375. {
  376. QMenu* menu = menuBar()->addMenu("&View");
  377. menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
  378. bool canvasLoaded = GetCanvas().IsValid();
  379. // Zoom options
  380. {
  381. // Zoom in
  382. {
  383. QAction* action = new QAction("Zoom &In", this);
  384. action->setShortcut(QKeySequence::ZoomIn);
  385. action->setEnabled(canvasLoaded);
  386. QObject::connect(action,
  387. &QAction::triggered,
  388. this,
  389. [this]([[maybe_unused]] bool checked)
  390. {
  391. GetViewport()->GetViewportInteraction()->IncreaseCanvasToViewportScale();
  392. });
  393. menu->addAction(action);
  394. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  395. }
  396. // Zoom out
  397. {
  398. QAction* action = new QAction("Zoom &Out", this);
  399. action->setShortcut(QKeySequence::ZoomOut);
  400. action->setEnabled(canvasLoaded);
  401. QObject::connect(action,
  402. &QAction::triggered,
  403. this,
  404. [this]([[maybe_unused]] bool checked)
  405. {
  406. GetViewport()->GetViewportInteraction()->DecreaseCanvasToViewportScale();
  407. });
  408. menu->addAction(action);
  409. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  410. }
  411. // Zoom to fit
  412. {
  413. QAction* action = new QAction("&Fit Canvas", this);
  414. {
  415. action->setShortcuts(QList<QKeySequence>{QKeySequence(Qt::CTRL + Qt::Key_0),
  416. QKeySequence(Qt::META + Qt::Key_0)});
  417. }
  418. action->setEnabled(canvasLoaded);
  419. QObject::connect(action,
  420. &QAction::triggered,
  421. this,
  422. [this]([[maybe_unused]] bool checked)
  423. {
  424. GetViewport()->GetViewportInteraction()->CenterCanvasInViewport();
  425. });
  426. menu->addAction(action);
  427. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  428. }
  429. // Actual size
  430. {
  431. QAction* action = new QAction("Actual &Size", this);
  432. {
  433. action->setShortcuts(QList<QKeySequence>{QKeySequence(Qt::CTRL + Qt::Key_1),
  434. QKeySequence(Qt::META + Qt::Key_1)});
  435. }
  436. action->setEnabled(canvasLoaded);
  437. QObject::connect(action,
  438. &QAction::triggered,
  439. this,
  440. [this]([[maybe_unused]] bool checked)
  441. {
  442. // Center the canvas then update scale
  443. GetViewport()->GetViewportInteraction()->CenterCanvasInViewport();
  444. GetViewport()->GetViewportInteraction()->ResetCanvasToViewportScale();
  445. });
  446. menu->addAction(action);
  447. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  448. }
  449. }
  450. menu->addSeparator();
  451. // Add all Edit mode QDockWidget panes.
  452. {
  453. QList<QDockWidget*> list = findChildren<QDockWidget*>();
  454. for (auto p : list)
  455. {
  456. // findChildren is recursive. But we only want dock widgets that are immediate
  457. // children since the Animation pane has some dock widgets of its own
  458. if (p->parent() == this && !IsPreviewModeDockWidget(p))
  459. {
  460. menu->addAction(p->toggleViewAction());
  461. }
  462. }
  463. }
  464. // Add all Edit mode QToolBar panes.
  465. {
  466. QList<QToolBar*> list = findChildren<QToolBar*>();
  467. for (auto p : list)
  468. {
  469. if (p->parent() == this && !IsPreviewModeToolbar(p))
  470. {
  471. menu->addAction(p->toggleViewAction());
  472. }
  473. }
  474. }
  475. menu->addSeparator();
  476. // Add menu item to hide/show the rulers
  477. {
  478. QAction* action = new QAction("&Rulers", this);
  479. action->setCheckable(true);
  480. action->setChecked(GetViewport() ? GetViewport()->AreRulersShown() : false);
  481. action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
  482. action->setEnabled(canvasLoaded);
  483. QObject::connect(action,
  484. &QAction::triggered,
  485. [this](bool checked)
  486. {
  487. // Set the visibility of the rulers
  488. GetViewport()->ShowRulers(checked);
  489. });
  490. menu->addAction(action);
  491. addAction(action);
  492. }
  493. // Add menu item to hide/show the guides
  494. {
  495. QAction* action = new QAction("&Guides", this);
  496. action->setCheckable(true);
  497. action->setChecked(GetViewport() ? GetViewport()->AreGuidesShown() : false);
  498. action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Semicolon));
  499. action->setEnabled(canvasLoaded);
  500. QObject::connect(action,
  501. &QAction::triggered,
  502. [this](bool checked)
  503. {
  504. // Set the visibility of the guides
  505. GetViewport()->ShowGuides(checked);
  506. });
  507. menu->addAction(action);
  508. addAction(action);
  509. }
  510. // Add menu item to lock the guides
  511. {
  512. QAction* action = new QAction("Lock Guides", this);
  513. action->setCheckable(true);
  514. action->setChecked(GuideHelpers::AreGuidesLocked(GetCanvas()));
  515. action->setEnabled(canvasLoaded);
  516. QObject::connect(action,
  517. &QAction::triggered,
  518. [this](bool checked)
  519. {
  520. // Set whether the guides are locked
  521. AZStd::string canvasUndoXml = CanvasHelpers::BeginUndoableCanvasChange(GetCanvas());
  522. GuideHelpers::SetGuidesAreLocked(GetCanvas(), checked);
  523. CanvasHelpers::EndUndoableCanvasChange(this, "toggle guides locked", canvasUndoXml);
  524. });
  525. menu->addAction(action);
  526. addAction(action);
  527. }
  528. // Add menu item to clear the guides
  529. {
  530. QAction* action = new QAction("Clear Guides", this);
  531. action->setEnabled(canvasLoaded);
  532. QObject::connect(action,
  533. &QAction::triggered,
  534. [this]([[maybe_unused]] bool checked)
  535. {
  536. // Clear guides
  537. AZStd::string canvasUndoXml = CanvasHelpers::BeginUndoableCanvasChange(GetCanvas());
  538. UiEditorCanvasBus::Event(GetCanvas(), &UiEditorCanvasBus::Events::RemoveAllGuides);
  539. CanvasHelpers::EndUndoableCanvasChange(this, "clear guides", canvasUndoXml);
  540. });
  541. menu->addAction(action);
  542. addAction(action);
  543. }
  544. menu->addSeparator();
  545. // Add sub-menu to control which elements have borders drawn on them
  546. {
  547. QMenu* drawElementBordersMenu = menu->addMenu("Draw &Borders on Unselected Elements");
  548. auto viewport = GetViewport();
  549. // Add option to draw borders on all unselected elements (subject to "Include" options below)
  550. {
  551. QAction* action = new QAction("&Draw Borders", this);
  552. action->setCheckable(true);
  553. action->setChecked(canvasLoaded ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Unselected) : false);
  554. action->setEnabled(canvasLoaded);
  555. QObject::connect(action,
  556. &QAction::triggered,
  557. viewport,
  558. [viewport, this]([[maybe_unused]] bool checked)
  559. {
  560. viewport->ToggleDrawElementBorders(ViewportWidget::DrawElementBorders_Unselected);
  561. RefreshEditorMenu();
  562. });
  563. drawElementBordersMenu->addAction(action);
  564. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  565. }
  566. // Add option to include visual elements.
  567. {
  568. QAction* action = new QAction("Include &Visual Elements", this);
  569. action->setCheckable(true);
  570. action->setChecked(canvasLoaded ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Visual) : false);
  571. action->setEnabled(canvasLoaded ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Unselected) : false);
  572. QObject::connect(action,
  573. &QAction::triggered,
  574. viewport,
  575. [viewport]([[maybe_unused]] bool checked)
  576. {
  577. viewport->ToggleDrawElementBorders(ViewportWidget::DrawElementBorders_Visual);
  578. });
  579. drawElementBordersMenu->addAction(action);
  580. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  581. }
  582. // Add option to include parent elements.
  583. {
  584. QAction* action = new QAction("Include &Parent Elements", this);
  585. action->setCheckable(true);
  586. action->setChecked(canvasLoaded ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Parent) : false);
  587. action->setEnabled(canvasLoaded ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Unselected) : false);
  588. QObject::connect(action,
  589. &QAction::triggered,
  590. viewport,
  591. [viewport]([[maybe_unused]] bool checked)
  592. {
  593. viewport->ToggleDrawElementBorders(ViewportWidget::DrawElementBorders_Parent);
  594. });
  595. drawElementBordersMenu->addAction(action);
  596. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  597. }
  598. // Add option to include hidden elements.
  599. {
  600. QAction* action = new QAction("Include &Hidden Elements", this);
  601. action->setCheckable(true);
  602. action->setChecked(viewport ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Hidden) : false);
  603. action->setEnabled(viewport ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Unselected) : false);
  604. QObject::connect(action,
  605. &QAction::triggered,
  606. viewport,
  607. [viewport]([[maybe_unused]] bool checked)
  608. {
  609. viewport->ToggleDrawElementBorders(ViewportWidget::DrawElementBorders_Hidden);
  610. });
  611. drawElementBordersMenu->addAction(action);
  612. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  613. }
  614. }
  615. AddMenu_View_LanguageSetting(menu);
  616. // Reload all fonts
  617. {
  618. QAction* action = new QAction("Reload All Fonts", this);
  619. {
  620. action->setShortcuts(QList<QKeySequence>{QKeySequence(Qt::CTRL + Qt::Key_L),
  621. QKeySequence(Qt::META + Qt::Key_L)});
  622. }
  623. action->setEnabled(canvasLoaded);
  624. QObject::connect(action,
  625. &QAction::triggered,
  626. []([[maybe_unused]] bool checked)
  627. {
  628. gEnv->pCryFont->ReloadAllFonts();
  629. });
  630. menu->addAction(action);
  631. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  632. }
  633. }
  634. void EditorWindow::AddMenu_View_LanguageSetting(QMenu* viewMenu)
  635. {
  636. QMenu* setCurrentLanguageMenu = viewMenu->addMenu("Set Current &Language");
  637. // Group language settings together by action group to only allow one
  638. // selection/language to be active at a time
  639. QActionGroup* actionGroup = new QActionGroup(setCurrentLanguageMenu);
  640. // Iterate through the subdirectories of the localization folder. Each
  641. // directory corresponds to a different language containing localization
  642. // translations for that language.
  643. AZStd::string fullLocPath(AZStd::string(gEnv->pFileIO->GetAlias("@products@")) + "/" + AZStd::string(m_startupLocFolderName.toUtf8().constData()));
  644. QDir locDir(fullLocPath.c_str());
  645. locDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
  646. locDir.setSorting(QDir::Name);
  647. QFileInfoList infoList(locDir.entryInfoList());
  648. for (auto subDirectory : infoList)
  649. {
  650. QString directoryName(subDirectory.fileName().toLower());
  651. // The loc system expects XML assets stored in a language-specific
  652. // folder with an "_xml" suffix in the name. Truncate the displayed
  653. // name so the user just sees the language name (this isn't required
  654. // though).
  655. const QString xmlPostFix("_xml");
  656. if (directoryName.endsWith(xmlPostFix))
  657. {
  658. directoryName.chop(xmlPostFix.length());
  659. }
  660. QAction* action = setCurrentLanguageMenu->addAction(directoryName);
  661. action->setCheckable(true);
  662. // When a language is selected, update the localization folder CVar
  663. QObject::connect(action,
  664. &QAction::triggered,
  665. this,
  666. [this, directoryName]([[maybe_unused]] bool checked)
  667. {
  668. // First try to locate the directory by name, without the "_xml"
  669. // suffix (in case it actually exists by this name).
  670. QString fullLocPath(QString(gEnv->pFileIO->GetAlias("@products@")) + "/" + m_startupLocFolderName + "/" + directoryName);
  671. QDir locDir(fullLocPath);
  672. // Try the directory with the expected suffix
  673. if (!locDir.exists())
  674. {
  675. locDir.setPath(locDir.path() + "_xml");
  676. }
  677. // Once the new CVar value is set, the loc system will auto-parse
  678. // the folder contents. See CSystem::OnLocalizationFolderCVarChanged.
  679. ICVar* locFolderCvar = gEnv->pConsole->GetCVar("sys_localization_folder");
  680. AZ_Assert(locFolderCvar,
  681. "sys_localization_folder no longer defined! This should be created in CSystem::CreateSystemVars().");
  682. if (locFolderCvar)
  683. {
  684. locFolderCvar->Set(locDir.path().toUtf8().constData());
  685. // Might as well throw a message if our dependencies change
  686. AZ_Assert(
  687. locFolderCvar->GetOnChangeCallback(),
  688. "sys_localization_folder CVar callback missing! "
  689. "This used to be set to CSystem::OnLocalizationFolderCVarChanged but is now missing. "
  690. "UI Editor language-switching features are no longer working.");
  691. }
  692. // Update the language setting; this will allow font families to
  693. // load language-specific font assets
  694. ICVar* languageCvar = gEnv->pConsole->GetCVar("g_language");
  695. AZ_Assert(languageCvar,
  696. "g_language no longer defined! This should be created in CSystem::CreateSystemVars().");
  697. if (languageCvar)
  698. {
  699. languageCvar->Set(directoryName.toUtf8().constData());
  700. // Make sure that our callback pipeline is setup properly
  701. AZ_Assert(
  702. languageCvar->GetOnChangeCallback(),
  703. "g_language CVar callback missing! "
  704. "This used to be set to CSystem::OnLangaugeCVarChanged but is now missing. "
  705. "UI Editor language-switching features are no longer working.");
  706. }
  707. });
  708. actionGroup->addAction(action);
  709. }
  710. }
  711. void EditorWindow::AddMenu_Preview()
  712. {
  713. QMenu* menu = menuBar()->addMenu("&Preview");
  714. menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
  715. // Toggle preview.
  716. {
  717. QString menuItemName;
  718. if (m_editorMode == UiEditorMode::Edit)
  719. {
  720. menuItemName = "&Preview";
  721. }
  722. else
  723. {
  724. menuItemName = "End &Preview";
  725. }
  726. QAction* action = new QAction(menuItemName, this);
  727. action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P));
  728. action->setEnabled(GetCanvas().IsValid());
  729. QObject::connect(action,
  730. &QAction::triggered,
  731. this,
  732. &EditorWindow::ToggleEditorMode);
  733. menu->addAction(action);
  734. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  735. }
  736. }
  737. void EditorWindow::AddMenu_PreviewView()
  738. {
  739. QMenu* menu = menuBar()->addMenu("&View");
  740. menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
  741. // Add all Preview mode QDockWidget panes.
  742. {
  743. QList<QDockWidget*> list = findChildren<QDockWidget*>();
  744. for (auto p : list)
  745. {
  746. // findChildren is recursive. But we only want dock widgets that are immediate
  747. // children since the Animation pane has some dock widgets of its own
  748. if (p->parent() == this && IsPreviewModeDockWidget(p))
  749. {
  750. menu->addAction(p->toggleViewAction());
  751. }
  752. }
  753. }
  754. // Add all Preview mode QToolBar panes.
  755. {
  756. QList<QToolBar*> list = findChildren<QToolBar*>();
  757. for (auto p : list)
  758. {
  759. if (p->parent() == this && IsPreviewModeToolbar(p))
  760. {
  761. menu->addAction(p->toggleViewAction());
  762. }
  763. }
  764. }
  765. }
  766. void EditorWindow::AddMenu_Help()
  767. {
  768. const char* documentationUrl = "https://o3de.org/docs/user-guide/interactivity/user-interface/";
  769. const char* tutorialsUrl = "https://o3de.org/docs/learning-guide/tutorials/";
  770. const char* forumUrl = "https://o3de.org/community/";
  771. QMenu* menu = menuBar()->addMenu("&Help");
  772. menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
  773. // Documentation
  774. {
  775. QAction* action = new QAction("&Documentation", this);
  776. QObject::connect(action,
  777. &QAction::triggered,
  778. this,
  779. [documentationUrl]([[maybe_unused]] bool checked)
  780. {
  781. QDesktopServices::openUrl(QUrl(documentationUrl));
  782. });
  783. menu->addAction(action);
  784. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  785. }
  786. // Tutorials
  787. {
  788. QAction* action = new QAction("&Tutorials", this);
  789. QObject::connect(action,
  790. &QAction::triggered,
  791. this,
  792. [tutorialsUrl]([[maybe_unused]] bool checked)
  793. {
  794. QDesktopServices::openUrl(QUrl(tutorialsUrl));
  795. });
  796. menu->addAction(action);
  797. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  798. }
  799. // Forum
  800. {
  801. QAction* action = new QAction("&Forum", this);
  802. QObject::connect(action,
  803. &QAction::triggered,
  804. this,
  805. [forumUrl]([[maybe_unused]] bool checked)
  806. {
  807. QDesktopServices::openUrl(QUrl(forumUrl));
  808. });
  809. menu->addAction(action);
  810. addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
  811. }
  812. }
  813. void EditorWindow::UpdateActionsEnabledState()
  814. {
  815. bool itemsAreSelected = !GetHierarchy()->selectedItems().isEmpty();
  816. bool thereIsContentInTheClipboard = ClipboardContainsOurDataType();
  817. for (QAction* action : m_actionsEnabledWithSelection)
  818. {
  819. action->setEnabled(itemsAreSelected);
  820. }
  821. if (m_pasteAsSiblingAction)
  822. {
  823. m_pasteAsSiblingAction->setEnabled(thereIsContentInTheClipboard);
  824. }
  825. if (m_pasteAsChildAction)
  826. {
  827. m_pasteAsChildAction->setEnabled(thereIsContentInTheClipboard && itemsAreSelected);
  828. }
  829. bool alignAllowed = ViewportAlign::IsAlignAllowed(this);
  830. for (QAction* action : m_actionsEnabledWithAlignAllowed)
  831. {
  832. action->setEnabled(alignAllowed);
  833. }
  834. GetModeToolbar()->GetAlignToolbarSection()->SetIsEnabled(alignAllowed);
  835. }
  836. void EditorWindow::RefreshEditorMenu()
  837. {
  838. m_actionsEnabledWithSelection.clear();
  839. m_pasteAsSiblingAction = nullptr;
  840. m_pasteAsChildAction = nullptr;
  841. m_actionsEnabledWithAlignAllowed.clear();
  842. auto actionList = actions();
  843. for (QAction* action : actionList)
  844. {
  845. removeAction(action);
  846. action->deleteLater();
  847. }
  848. menuBar()->clear();
  849. if (GetEditorMode() == UiEditorMode::Edit)
  850. {
  851. AddMenu_File();
  852. AddMenu_Edit();
  853. AddMenu_View();
  854. AddMenu_Preview();
  855. AddMenu_Help();
  856. }
  857. else
  858. {
  859. AddMenu_PreviewView();
  860. AddMenu_Preview();
  861. AddMenu_Help();
  862. }
  863. // Lastly, set up shortcuts that aren't on the menu since all actions were removed above
  864. SetupShortcuts();
  865. }
  866. void EditorWindow::SetupShortcuts()
  867. {
  868. // Actions with shortcuts are created instead of direct shortcuts because the shortcut dispatcher only looks for matching actions
  869. // Cycle coordinate system
  870. {
  871. QAction* action = new QAction("Coordinate System Cycle", this);
  872. action->setShortcut(QKeySequence(UICANVASEDITOR_COORDINATE_SYSTEM_CYCLE_SHORTCUT_KEY_SEQUENCE));
  873. QObject::connect(action,
  874. &QAction::triggered,
  875. [this]()
  876. {
  877. SignalCoordinateSystemCycle();
  878. });
  879. addAction(action);
  880. }
  881. // Toggle Snap to Grid
  882. {
  883. QAction* action = new QAction("Snap to Grid Toggle", this);
  884. action->setShortcut(QKeySequence(UICANVASEDITOR_SNAP_TO_GRID_TOGGLE_SHORTCUT_KEY_SEQUENCE));
  885. QObject::connect(action,
  886. &QAction::triggered,
  887. [this]()
  888. {
  889. SignalSnapToGridToggle();
  890. });
  891. addAction(action);
  892. }
  893. }
  894. void DisplayNullMetadataMessage(EditorWindow* editorWindow)
  895. {
  896. QMessageBox(QMessageBox::Critical,
  897. "Error",
  898. QString::fromUtf8("Unable to save: canvas metadata is null. Please try reopening the canvas."),
  899. QMessageBox::Ok, editorWindow).exec();
  900. }
  901. QAction* EditorWindow::CreateSaveCanvasAction(AZ::EntityId canvasEntityId, bool forContextMenu)
  902. {
  903. UiCanvasMetadata *canvasMetadata = canvasEntityId.IsValid() ? GetCanvasMetadata(canvasEntityId) : nullptr;
  904. AZStd::string canvasSourcePathname;
  905. AZStd::string canvasFilename;
  906. if (canvasMetadata)
  907. {
  908. canvasSourcePathname = canvasMetadata->m_canvasSourceAssetPathname;
  909. UiCanvasBus::EventResult(canvasFilename, canvasEntityId, &UiCanvasBus::Events::GetPathname);
  910. }
  911. QFileInfo fileInfo(canvasSourcePathname.c_str());
  912. QAction* action = new QAction(QString("&Save " + (fileInfo.fileName().isEmpty() ? "Canvas" : fileInfo.fileName())), this);
  913. if (!forContextMenu && !canvasFilename.empty())
  914. {
  915. action->setShortcut(QKeySequence::Save);
  916. }
  917. // If there's no filename,
  918. // we want the menu to be visible, but disabled.
  919. action->setEnabled(!canvasFilename.empty());
  920. QObject::connect(action,
  921. &QAction::triggered,
  922. this,
  923. [this, canvasEntityId]([[maybe_unused]] bool checked)
  924. {
  925. UiCanvasMetadata *canvasMetadata = GetCanvasMetadata(canvasEntityId);
  926. AZ_Assert(canvasMetadata, "Canvas metadata not found");
  927. if (canvasMetadata)
  928. {
  929. bool ok = SaveCanvasToXml(*canvasMetadata, false);
  930. if (!ok)
  931. {
  932. return;
  933. }
  934. // Refresh the File menu to update the
  935. // "Recent Files" and "Save".
  936. RefreshEditorMenu();
  937. }
  938. else
  939. {
  940. DisplayNullMetadataMessage(this);
  941. }
  942. });
  943. return action;
  944. }
  945. QAction* EditorWindow::CreateSaveCanvasAsAction(AZ::EntityId canvasEntityId, bool forContextMenu)
  946. {
  947. UiCanvasMetadata *canvasMetadata = canvasEntityId.IsValid() ? GetCanvasMetadata(canvasEntityId) : nullptr;
  948. AZStd::string canvasSourcePathname;
  949. AZStd::string canvasFilename;
  950. if (canvasMetadata)
  951. {
  952. canvasSourcePathname = canvasMetadata->m_canvasSourceAssetPathname;
  953. UiCanvasBus::EventResult(canvasFilename, canvasEntityId, &UiCanvasBus::Events::GetPathname);
  954. }
  955. QAction* action = new QAction("Save Canvas &As...", this);
  956. if (!forContextMenu && canvasFilename.empty())
  957. {
  958. action->setShortcut(QKeySequence::Save);
  959. }
  960. action->setEnabled(canvasMetadata);
  961. QObject::connect(action,
  962. &QAction::triggered,
  963. this,
  964. [this, canvasEntityId]([[maybe_unused]] bool checked)
  965. {
  966. UiCanvasMetadata *canvasMetadata = GetCanvasMetadata(canvasEntityId);
  967. AZ_Assert(canvasMetadata, "Canvas metadata not found");
  968. if (canvasMetadata)
  969. {
  970. bool ok = SaveCanvasToXml(*canvasMetadata, true);
  971. if (!ok)
  972. {
  973. return;
  974. }
  975. // Refresh the File menu to update the
  976. // "Recent Files" and "Save".
  977. RefreshEditorMenu();
  978. }
  979. else
  980. {
  981. DisplayNullMetadataMessage(this);
  982. }
  983. });
  984. return action;
  985. }
  986. QAction* EditorWindow::CreateSaveSliceAction(UiCanvasMetadata *canvasMetadata, bool forContextMenu)
  987. {
  988. // We will never call this function unless canvasMetadata is non null and m_isSliceEditing is true
  989. AZ_Assert(canvasMetadata && canvasMetadata->m_isSliceEditing, "CreateSaveSliceAction requires valid canvas metadata and to be in slice editing mode");
  990. // as a safeguard check that the entity still exists
  991. AZ::EntityId sliceEntityId = canvasMetadata->m_sliceEntityId;
  992. AZ::Entity* sliceEntity = nullptr;
  993. AZ::ComponentApplicationBus::BroadcastResult(sliceEntity, &AZ::ComponentApplicationBus::Events::FindEntity, sliceEntityId);
  994. if (!sliceEntity)
  995. {
  996. // Slice entity not found, disable the menu item but also change it to indicate the error
  997. QAction* action = new QAction(QString("&Save Slice (slice entity not found)"), this);
  998. action->setEnabled(false);
  999. return action;
  1000. }
  1001. // get the slice address
  1002. AZ::SliceComponent::SliceInstanceAddress sliceAddress;
  1003. AzFramework::SliceEntityRequestBus::EventResult(sliceAddress, canvasMetadata->m_sliceEntityId,
  1004. &AzFramework::SliceEntityRequestBus::Events::GetOwningSlice);
  1005. // if isSliceEntity is false then something is wrong. The user could have done a detach slice for example
  1006. if (!sliceAddress.IsValid() || !sliceAddress.GetReference()->GetSliceAsset())
  1007. {
  1008. // Slice entity is no longer a slice instance, disable the menu item but also change it to indicate the error
  1009. QAction* action = new QAction(QString("&Save Slice (slice entity is no longer an instance)"), this);
  1010. action->setEnabled(false);
  1011. return action;
  1012. }
  1013. AZStd::string canvasDisplayName = canvasMetadata->m_canvasDisplayName;
  1014. QAction* action = new QAction(QString("&Save ") + canvasDisplayName.c_str(), this);
  1015. if (!forContextMenu)
  1016. {
  1017. action->setShortcut(QKeySequence::Save);
  1018. action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
  1019. }
  1020. // There should always be a valid path for the slice but if there is not we disable the menu item.
  1021. action->setEnabled(!canvasDisplayName.empty());
  1022. QObject::connect(action,
  1023. &QAction::triggered,
  1024. [this, canvasMetadata]([[maybe_unused]] bool checked)
  1025. {
  1026. SaveSlice(*canvasMetadata);
  1027. });
  1028. return action;
  1029. }
  1030. QAction* EditorWindow::CreateSaveAllCanvasesAction([[maybe_unused]] bool forContextMenu)
  1031. {
  1032. QAction* action = new QAction(QString("Save All Canvases"), this);
  1033. action->setEnabled(m_canvasMetadataMap.size() > 0);
  1034. QObject::connect(action,
  1035. &QAction::triggered,
  1036. this,
  1037. [this]([[maybe_unused]] bool checked)
  1038. {
  1039. bool saved = false;
  1040. for (auto mapItem : m_canvasMetadataMap)
  1041. {
  1042. auto canvasMetadata = mapItem.second;
  1043. if (canvasMetadata->m_isSliceEditing)
  1044. {
  1045. saved |= SaveSlice(*canvasMetadata);
  1046. }
  1047. else
  1048. {
  1049. saved |= SaveCanvasToXml(*canvasMetadata, false);
  1050. }
  1051. }
  1052. if (saved)
  1053. {
  1054. // Refresh the File menu to update the
  1055. // "Recent Files" and "Save".
  1056. RefreshEditorMenu();
  1057. }
  1058. });
  1059. return action;
  1060. }
  1061. QAction* EditorWindow::CreateCloseCanvasAction(AZ::EntityId canvasEntityId, bool forContextMenu)
  1062. {
  1063. QAction* action = new QAction("&Close Canvas", this);
  1064. if (!forContextMenu)
  1065. {
  1066. action->setShortcut(QKeySequence::Close);
  1067. }
  1068. action->setEnabled(canvasEntityId.IsValid());
  1069. QObject::connect(action,
  1070. &QAction::triggered,
  1071. this,
  1072. [this, canvasEntityId]([[maybe_unused]] bool checked)
  1073. {
  1074. CloseCanvas(canvasEntityId);
  1075. });
  1076. return action;
  1077. }
  1078. QAction* EditorWindow::CreateCloseAllOtherCanvasesAction(AZ::EntityId canvasEntityId, bool forContextMenu)
  1079. {
  1080. QAction* action = new QAction(forContextMenu ? "Close All but This Canvas" : "Close All but Active Canvas", this);
  1081. action->setEnabled(m_canvasMetadataMap.size() > 1);
  1082. QObject::connect(action,
  1083. &QAction::triggered,
  1084. this,
  1085. [this, canvasEntityId]([[maybe_unused]] bool checked)
  1086. {
  1087. CloseAllOtherCanvases(canvasEntityId);
  1088. });
  1089. return action;
  1090. }
  1091. QAction* EditorWindow::CreateCloseAllCanvasesAction([[maybe_unused]] bool forContextMenu)
  1092. {
  1093. QAction* action = new QAction("Close All Canvases", this);
  1094. action->setEnabled(m_canvasMetadataMap.size() > 0);
  1095. QObject::connect(action,
  1096. &QAction::triggered,
  1097. this,
  1098. &EditorWindow::CloseAllCanvases);
  1099. return action;
  1100. }