1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "EditorCommon.h"
- #include <AzQtComponents/Buses/ShortcutDispatch.h>
- #include <AzToolsFramework/Slice/SliceUtilities.h>
- #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
- #include "AlignToolbarSection.h"
- #include "ViewportAlign.h"
- #include "CanvasHelpers.h"
- #include "GuideHelpers.h"
- #include <LyShine/Bus/UiEditorCanvasBus.h>
- #include <Util/PathUtil.h>
- #include <QFileDialog>
- #include <QMenuBar>
- #include <QUndoGroup>
- #include <QUndoView>
- #include <QDesktopServices>
- #include <QMessageBox>
- #include <QDockWidget>
- static const bool debugViewUndoStack = false;
- QAction* EditorWindow::AddMenuAction(const QString& text, bool enabled, QMenu* menu, AZStd::function<void (bool)> function)
- {
- QAction* action = new QAction(text, this);
- action->setEnabled(enabled);
- QObject::connect(action,
- &QAction::triggered,
- function);
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- m_actionsEnabledWithSelection.push_back(action);
- return action;
- }
- void EditorWindow::EditorMenu_Open(QString optional_selectedFile)
- {
- if (optional_selectedFile.isEmpty())
- {
- QString dir;
- QStringList recentFiles = ReadRecentFiles();
- // If we had recently opened canvases, open the most recent one's directory
- if (recentFiles.size() > 0)
- {
- dir = Path::GetPath(recentFiles.front());
- }
- // Else go to the default canvas directory
- else
- {
- dir = FileHelpers::GetAbsoluteDir(UICANVASEDITOR_CANVAS_DIRECTORY);
- }
- QFileDialog dialog(this, QString(), dir, "*." UICANVASEDITOR_CANVAS_EXTENSION);
- dialog.setFileMode(QFileDialog::ExistingFiles);
- if (dialog.exec() == QDialog::Accepted)
- {
- OpenCanvases(dialog.selectedFiles());
- }
- }
- else
- {
- OpenCanvas(optional_selectedFile);
- }
- }
- void EditorWindow::AddMenu_File()
- {
- QMenu* menu = menuBar()->addMenu("&File");
- menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
- // Create a new canvas.
- {
- QAction* action = new QAction("&New Canvas", this);
- action->setShortcut(QKeySequence::New);
- QObject::connect(action,
- &QAction::triggered,
- this,
- &EditorWindow::NewCanvas);
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Load a canvas.
- {
- QAction* action = new QAction("&Open Canvas...", this);
- action->setShortcut(QKeySequence::Open);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [ this ]([[maybe_unused]] bool checked)
- {
- EditorMenu_Open("");
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- bool canvasLoaded = GetCanvas().IsValid();
- menu->addSeparator();
- UiCanvasMetadata *canvasMetadata = canvasLoaded ? GetCanvasMetadata(GetCanvas()) : nullptr;
- if (canvasMetadata && canvasMetadata->m_isSliceEditing)
- {
- // Save the slice
- {
- QAction *action = CreateSaveSliceAction(canvasMetadata);
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- }
- else
- {
- // Save the canvas
- {
- QAction *action = CreateSaveCanvasAction(GetCanvas());
- menu->addAction(action);
- addAction(action);
- }
- // Save the canvas with new file name
- {
- QAction* action = CreateSaveCanvasAsAction(GetCanvas());
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- }
- // Save all the canvases
- {
- QAction* action = CreateSaveAllCanvasesAction();
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- menu->addSeparator();
- // Close the active canvas
- {
- QAction* action = CreateCloseCanvasAction(GetCanvas());
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Close all canvases
- {
- QAction* action = CreateCloseAllCanvasesAction();
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Close all but the active canvas
- {
- QAction* action = CreateCloseAllOtherCanvasesAction(GetCanvas());
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- menu->addSeparator();
- // Recent Files.
- {
- QStringList recentFiles = ReadRecentFiles();
- // List of recent files.
- {
- QMenu* recentMenu = menu->addMenu("&Recent Files");
- recentMenu->setEnabled(!recentFiles.isEmpty());
- // QStringList -> QMenu.
- for (auto && fileName : recentFiles)
- {
- QAction* action = new QAction(fileName, this);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [ this, fileName ]([[maybe_unused]] bool checked)
- {
- EditorMenu_Open(fileName);
- });
- recentMenu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- }
- // Clear Recent Files.
- {
- QAction* action = new QAction("Clear Recent Files", this);
- action->setEnabled(!recentFiles.isEmpty());
- QObject::connect(action,
- &QAction::triggered,
- this,
- [ this ]([[maybe_unused]] bool checked)
- {
- ClearRecentFile();
- RefreshEditorMenu();
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- }
- }
- void EditorWindow::AddMenuItems_Edit(QMenu* menu)
- {
- // Undo.
- {
- QAction* action = GetUndoGroup()->createUndoAction(this);
- action->setShortcut(QKeySequence::Undo);
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Redo.
- {
- QAction* action = GetUndoGroup()->createRedoAction(this);
- // IMPORTANT: We CAN'T just provide QKeySequence::Redo as a
- // shortcut because the menu will show CTRL+Y as the shortcut.
- // To display CTRL+SHIFT+Z by default, we have to provide the
- // list of shortcuts explicitly.
- {
- action->setShortcuts(QList<QKeySequence>{AzQtComponents::RedoKeySequence,
- QKeySequence(Qt::META + Qt::SHIFT + Qt::Key_Z),
- QKeySequence(QKeySequence::Redo)});
- }
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- bool canvasLoaded = GetCanvas().IsValid();
- menu->addSeparator();
- // Select All.
- {
- QAction* action = new QAction("Select &All", this);
- action->setShortcut(QKeySequence::SelectAll);
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- GetHierarchy(),
- &HierarchyWidget::selectAll);
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- menu->addSeparator();
- bool itemsAreSelected = !GetHierarchy()->selectedItems().isEmpty();
- bool thereIsContentInTheClipboard = ClipboardContainsOurDataType();
- // Cut.
- {
- QAction* action = new QAction("Cu&t", this);
- action->setShortcut(QKeySequence::Cut);
- action->setEnabled(itemsAreSelected);
- QObject::connect(action,
- &QAction::triggered,
- GetHierarchy(),
- &HierarchyWidget::Cut);
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- m_actionsEnabledWithSelection.push_back(action);
- }
- // Copy.
- {
- QAction* action = new QAction("&Copy", this);
- action->setShortcut(QKeySequence::Copy);
- action->setEnabled(itemsAreSelected);
- QObject::connect(action,
- &QAction::triggered,
- GetHierarchy(),
- &HierarchyWidget::Copy);
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- m_actionsEnabledWithSelection.push_back(action);
- }
- // Paste.
- {
- // Paste as silbing.
- {
- QAction* action = new QAction(itemsAreSelected ? "&Paste as sibling" : "&Paste", this);
- action->setShortcut(QKeySequence::Paste);
- action->setEnabled(canvasLoaded && thereIsContentInTheClipboard);
- QObject::connect(action,
- &QAction::triggered,
- GetHierarchy(),
- &HierarchyWidget::PasteAsSibling);
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- m_pasteAsSiblingAction = action;
- }
- // Paste as child.
- {
- QAction* action = new QAction("Paste as c&hild", this);
- {
- action->setShortcuts(QList<QKeySequence>{QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_V),
- QKeySequence(Qt::META + Qt::SHIFT + Qt::Key_V)});
- }
- action->setEnabled(canvasLoaded && thereIsContentInTheClipboard && itemsAreSelected);
- QObject::connect(action,
- &QAction::triggered,
- GetHierarchy(),
- &HierarchyWidget::PasteAsChild);
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- m_pasteAsChildAction = action;
- }
- }
- if (debugViewUndoStack)
- {
- QAction* action = new QAction("[DEBUG] View undo stack", this);
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [this]([[maybe_unused]] bool checked)
- {
- static QUndoView* undoView = nullptr;
- if (undoView)
- {
- undoView->setGroup(GetUndoGroup());
- }
- else
- {
- undoView = new QUndoView(GetUndoGroup());
- undoView->setWindowTitle("[DEBUG] Undo stack");
- undoView->setAttribute(Qt::WA_QuitOnClose, false);
- }
- undoView->show();
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- menu->addSeparator();
- // Find elements
- {
- QAction* action = new QAction("&Find Elements...", this);
- action->setShortcut(QKeySequence::Find);
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- [this]([[maybe_unused]] bool checked)
- {
- ShowEntitySearchModal();
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- menu->addSeparator();
- // Delete.
- {
- QAction* action = new QAction("Delete", this);
- action->setShortcut(QKeySequence::Delete);
- action->setEnabled(itemsAreSelected);
- QObject::connect(action,
- &QAction::triggered,
- GetHierarchy(),
- [this]() { GetHierarchy()->DeleteSelectedItems(); });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- m_actionsEnabledWithSelection.push_back(action);
- }
- // Add Align sub-menu
- {
- QMenu* alignMenu = menu->addMenu("Align");
- auto viewport = canvasLoaded ? GetViewport() : nullptr;
- bool enabled = viewport && itemsAreSelected && ViewportAlign::IsAlignAllowed(this);
- // Add each sub-menu item. Store the QActions so that we can enable/disable them when align is allowed or not.
- {
- m_actionsEnabledWithAlignAllowed.push_back(
- AddMenuAction("Top Edges", enabled, alignMenu,
- [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::VerticalTop); } ));
- m_actionsEnabledWithAlignAllowed.push_back(
- AddMenuAction("Vertical Centers", enabled, alignMenu,
- [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::VerticalCenter); } ));
- m_actionsEnabledWithAlignAllowed.push_back(
- AddMenuAction("Bottom Edges", enabled, alignMenu,
- [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::VerticalBottom); } ));
- m_actionsEnabledWithAlignAllowed.push_back(
- AddMenuAction("Left Edges", enabled, alignMenu,
- [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::HorizontalLeft); } ));
- m_actionsEnabledWithAlignAllowed.push_back(
- AddMenuAction("Horizontal Centers", enabled, alignMenu,
- [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::HorizontalCenter); } ));
- m_actionsEnabledWithAlignAllowed.push_back(
- AddMenuAction("Right Edges", enabled, alignMenu,
- [this]([[maybe_unused]] bool checked) { ViewportAlign::AlignSelectedElements(this, ViewportAlign::AlignType::HorizontalRight); } ));
- }
- }
- }
- void EditorWindow::AddMenu_Edit()
- {
- QMenu* menu = menuBar()->addMenu("&Edit");
- menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
- AddMenuItems_Edit(menu);
- }
- void EditorWindow::AddMenu_View()
- {
- QMenu* menu = menuBar()->addMenu("&View");
- menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
- bool canvasLoaded = GetCanvas().IsValid();
- // Zoom options
- {
- // Zoom in
- {
- QAction* action = new QAction("Zoom &In", this);
- action->setShortcut(QKeySequence::ZoomIn);
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [this]([[maybe_unused]] bool checked)
- {
- GetViewport()->GetViewportInteraction()->IncreaseCanvasToViewportScale();
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Zoom out
- {
- QAction* action = new QAction("Zoom &Out", this);
- action->setShortcut(QKeySequence::ZoomOut);
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [this]([[maybe_unused]] bool checked)
- {
- GetViewport()->GetViewportInteraction()->DecreaseCanvasToViewportScale();
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Zoom to fit
- {
- QAction* action = new QAction("&Fit Canvas", this);
- {
- action->setShortcuts(QList<QKeySequence>{QKeySequence(Qt::CTRL + Qt::Key_0),
- QKeySequence(Qt::META + Qt::Key_0)});
- }
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [this]([[maybe_unused]] bool checked)
- {
- GetViewport()->GetViewportInteraction()->CenterCanvasInViewport();
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Actual size
- {
- QAction* action = new QAction("Actual &Size", this);
- {
- action->setShortcuts(QList<QKeySequence>{QKeySequence(Qt::CTRL + Qt::Key_1),
- QKeySequence(Qt::META + Qt::Key_1)});
- }
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [this]([[maybe_unused]] bool checked)
- {
- // Center the canvas then update scale
- GetViewport()->GetViewportInteraction()->CenterCanvasInViewport();
- GetViewport()->GetViewportInteraction()->ResetCanvasToViewportScale();
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- }
- menu->addSeparator();
- // Add all Edit mode QDockWidget panes.
- {
- QList<QDockWidget*> list = findChildren<QDockWidget*>();
- for (auto p : list)
- {
- // findChildren is recursive. But we only want dock widgets that are immediate
- // children since the Animation pane has some dock widgets of its own
- if (p->parent() == this && !IsPreviewModeDockWidget(p))
- {
- menu->addAction(p->toggleViewAction());
- }
- }
- }
- // Add all Edit mode QToolBar panes.
- {
- QList<QToolBar*> list = findChildren<QToolBar*>();
- for (auto p : list)
- {
- if (p->parent() == this && !IsPreviewModeToolbar(p))
- {
- menu->addAction(p->toggleViewAction());
- }
- }
- }
- menu->addSeparator();
- // Add menu item to hide/show the rulers
- {
- QAction* action = new QAction("&Rulers", this);
- action->setCheckable(true);
- action->setChecked(GetViewport() ? GetViewport()->AreRulersShown() : false);
- action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- [this](bool checked)
- {
- // Set the visibility of the rulers
- GetViewport()->ShowRulers(checked);
- });
- menu->addAction(action);
- addAction(action);
- }
- // Add menu item to hide/show the guides
- {
- QAction* action = new QAction("&Guides", this);
- action->setCheckable(true);
- action->setChecked(GetViewport() ? GetViewport()->AreGuidesShown() : false);
- action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Semicolon));
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- [this](bool checked)
- {
- // Set the visibility of the guides
- GetViewport()->ShowGuides(checked);
- });
- menu->addAction(action);
- addAction(action);
- }
- // Add menu item to lock the guides
- {
- QAction* action = new QAction("Lock Guides", this);
- action->setCheckable(true);
- action->setChecked(GuideHelpers::AreGuidesLocked(GetCanvas()));
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- [this](bool checked)
- {
- // Set whether the guides are locked
- AZStd::string canvasUndoXml = CanvasHelpers::BeginUndoableCanvasChange(GetCanvas());
- GuideHelpers::SetGuidesAreLocked(GetCanvas(), checked);
- CanvasHelpers::EndUndoableCanvasChange(this, "toggle guides locked", canvasUndoXml);
- });
- menu->addAction(action);
- addAction(action);
- }
- // Add menu item to clear the guides
- {
- QAction* action = new QAction("Clear Guides", this);
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- [this]([[maybe_unused]] bool checked)
- {
- // Clear guides
- AZStd::string canvasUndoXml = CanvasHelpers::BeginUndoableCanvasChange(GetCanvas());
- UiEditorCanvasBus::Event(GetCanvas(), &UiEditorCanvasBus::Events::RemoveAllGuides);
- CanvasHelpers::EndUndoableCanvasChange(this, "clear guides", canvasUndoXml);
- });
- menu->addAction(action);
- addAction(action);
- }
- menu->addSeparator();
- // Add sub-menu to control which elements have borders drawn on them
- {
- QMenu* drawElementBordersMenu = menu->addMenu("Draw &Borders on Unselected Elements");
- auto viewport = GetViewport();
- // Add option to draw borders on all unselected elements (subject to "Include" options below)
- {
- QAction* action = new QAction("&Draw Borders", this);
- action->setCheckable(true);
- action->setChecked(canvasLoaded ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Unselected) : false);
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- viewport,
- [viewport, this]([[maybe_unused]] bool checked)
- {
- viewport->ToggleDrawElementBorders(ViewportWidget::DrawElementBorders_Unselected);
- RefreshEditorMenu();
- });
- drawElementBordersMenu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Add option to include visual elements.
- {
- QAction* action = new QAction("Include &Visual Elements", this);
- action->setCheckable(true);
- action->setChecked(canvasLoaded ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Visual) : false);
- action->setEnabled(canvasLoaded ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Unselected) : false);
- QObject::connect(action,
- &QAction::triggered,
- viewport,
- [viewport]([[maybe_unused]] bool checked)
- {
- viewport->ToggleDrawElementBorders(ViewportWidget::DrawElementBorders_Visual);
- });
- drawElementBordersMenu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Add option to include parent elements.
- {
- QAction* action = new QAction("Include &Parent Elements", this);
- action->setCheckable(true);
- action->setChecked(canvasLoaded ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Parent) : false);
- action->setEnabled(canvasLoaded ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Unselected) : false);
- QObject::connect(action,
- &QAction::triggered,
- viewport,
- [viewport]([[maybe_unused]] bool checked)
- {
- viewport->ToggleDrawElementBorders(ViewportWidget::DrawElementBorders_Parent);
- });
- drawElementBordersMenu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Add option to include hidden elements.
- {
- QAction* action = new QAction("Include &Hidden Elements", this);
- action->setCheckable(true);
- action->setChecked(viewport ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Hidden) : false);
- action->setEnabled(viewport ? viewport->IsDrawingElementBorders(ViewportWidget::DrawElementBorders_Unselected) : false);
- QObject::connect(action,
- &QAction::triggered,
- viewport,
- [viewport]([[maybe_unused]] bool checked)
- {
- viewport->ToggleDrawElementBorders(ViewportWidget::DrawElementBorders_Hidden);
- });
- drawElementBordersMenu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- }
- AddMenu_View_LanguageSetting(menu);
- // Reload all fonts
- {
- QAction* action = new QAction("Reload All Fonts", this);
- {
- action->setShortcuts(QList<QKeySequence>{QKeySequence(Qt::CTRL + Qt::Key_L),
- QKeySequence(Qt::META + Qt::Key_L)});
- }
- action->setEnabled(canvasLoaded);
- QObject::connect(action,
- &QAction::triggered,
- []([[maybe_unused]] bool checked)
- {
- gEnv->pCryFont->ReloadAllFonts();
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- }
- void EditorWindow::AddMenu_View_LanguageSetting(QMenu* viewMenu)
- {
- QMenu* setCurrentLanguageMenu = viewMenu->addMenu("Set Current &Language");
- // Group language settings together by action group to only allow one
- // selection/language to be active at a time
- QActionGroup* actionGroup = new QActionGroup(setCurrentLanguageMenu);
- // Iterate through the subdirectories of the localization folder. Each
- // directory corresponds to a different language containing localization
- // translations for that language.
- AZStd::string fullLocPath(AZStd::string(gEnv->pFileIO->GetAlias("@products@")) + "/" + AZStd::string(m_startupLocFolderName.toUtf8().constData()));
- QDir locDir(fullLocPath.c_str());
- locDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
- locDir.setSorting(QDir::Name);
- QFileInfoList infoList(locDir.entryInfoList());
- for (auto subDirectory : infoList)
- {
- QString directoryName(subDirectory.fileName().toLower());
- // The loc system expects XML assets stored in a language-specific
- // folder with an "_xml" suffix in the name. Truncate the displayed
- // name so the user just sees the language name (this isn't required
- // though).
- const QString xmlPostFix("_xml");
- if (directoryName.endsWith(xmlPostFix))
- {
- directoryName.chop(xmlPostFix.length());
- }
- QAction* action = setCurrentLanguageMenu->addAction(directoryName);
- action->setCheckable(true);
- // When a language is selected, update the localization folder CVar
- QObject::connect(action,
- &QAction::triggered,
- this,
- [this, directoryName]([[maybe_unused]] bool checked)
- {
- // First try to locate the directory by name, without the "_xml"
- // suffix (in case it actually exists by this name).
- QString fullLocPath(QString(gEnv->pFileIO->GetAlias("@products@")) + "/" + m_startupLocFolderName + "/" + directoryName);
- QDir locDir(fullLocPath);
- // Try the directory with the expected suffix
- if (!locDir.exists())
- {
- locDir.setPath(locDir.path() + "_xml");
- }
- // Once the new CVar value is set, the loc system will auto-parse
- // the folder contents. See CSystem::OnLocalizationFolderCVarChanged.
- ICVar* locFolderCvar = gEnv->pConsole->GetCVar("sys_localization_folder");
- AZ_Assert(locFolderCvar,
- "sys_localization_folder no longer defined! This should be created in CSystem::CreateSystemVars().");
- if (locFolderCvar)
- {
- locFolderCvar->Set(locDir.path().toUtf8().constData());
- // Might as well throw a message if our dependencies change
- AZ_Assert(
- locFolderCvar->GetOnChangeCallback(),
- "sys_localization_folder CVar callback missing! "
- "This used to be set to CSystem::OnLocalizationFolderCVarChanged but is now missing. "
- "UI Editor language-switching features are no longer working.");
- }
- // Update the language setting; this will allow font families to
- // load language-specific font assets
- ICVar* languageCvar = gEnv->pConsole->GetCVar("g_language");
- AZ_Assert(languageCvar,
- "g_language no longer defined! This should be created in CSystem::CreateSystemVars().");
- if (languageCvar)
- {
- languageCvar->Set(directoryName.toUtf8().constData());
- // Make sure that our callback pipeline is setup properly
- AZ_Assert(
- languageCvar->GetOnChangeCallback(),
- "g_language CVar callback missing! "
- "This used to be set to CSystem::OnLangaugeCVarChanged but is now missing. "
- "UI Editor language-switching features are no longer working.");
- }
- });
- actionGroup->addAction(action);
- }
- }
- void EditorWindow::AddMenu_Preview()
- {
- QMenu* menu = menuBar()->addMenu("&Preview");
- menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
- // Toggle preview.
- {
- QString menuItemName;
- if (m_editorMode == UiEditorMode::Edit)
- {
- menuItemName = "&Preview";
- }
- else
- {
- menuItemName = "End &Preview";
- }
- QAction* action = new QAction(menuItemName, this);
- action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P));
- action->setEnabled(GetCanvas().IsValid());
- QObject::connect(action,
- &QAction::triggered,
- this,
- &EditorWindow::ToggleEditorMode);
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- }
- void EditorWindow::AddMenu_PreviewView()
- {
- QMenu* menu = menuBar()->addMenu("&View");
- menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
- // Add all Preview mode QDockWidget panes.
- {
- QList<QDockWidget*> list = findChildren<QDockWidget*>();
- for (auto p : list)
- {
- // findChildren is recursive. But we only want dock widgets that are immediate
- // children since the Animation pane has some dock widgets of its own
- if (p->parent() == this && IsPreviewModeDockWidget(p))
- {
- menu->addAction(p->toggleViewAction());
- }
- }
- }
- // Add all Preview mode QToolBar panes.
- {
- QList<QToolBar*> list = findChildren<QToolBar*>();
- for (auto p : list)
- {
- if (p->parent() == this && IsPreviewModeToolbar(p))
- {
- menu->addAction(p->toggleViewAction());
- }
- }
- }
- }
- void EditorWindow::AddMenu_Help()
- {
- const char* documentationUrl = "https://o3de.org/docs/user-guide/interactivity/user-interface/";
- const char* tutorialsUrl = "https://o3de.org/docs/learning-guide/tutorials/";
- const char* forumUrl = "https://o3de.org/community/";
- QMenu* menu = menuBar()->addMenu("&Help");
- menu->setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
- // Documentation
- {
- QAction* action = new QAction("&Documentation", this);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [documentationUrl]([[maybe_unused]] bool checked)
- {
- QDesktopServices::openUrl(QUrl(documentationUrl));
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Tutorials
- {
- QAction* action = new QAction("&Tutorials", this);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [tutorialsUrl]([[maybe_unused]] bool checked)
- {
- QDesktopServices::openUrl(QUrl(tutorialsUrl));
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- // Forum
- {
- QAction* action = new QAction("&Forum", this);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [forumUrl]([[maybe_unused]] bool checked)
- {
- QDesktopServices::openUrl(QUrl(forumUrl));
- });
- menu->addAction(action);
- addAction(action); // Also add the action to the window until the shortcut dispatcher can find the menu action
- }
- }
- void EditorWindow::UpdateActionsEnabledState()
- {
- bool itemsAreSelected = !GetHierarchy()->selectedItems().isEmpty();
- bool thereIsContentInTheClipboard = ClipboardContainsOurDataType();
- for (QAction* action : m_actionsEnabledWithSelection)
- {
- action->setEnabled(itemsAreSelected);
- }
- if (m_pasteAsSiblingAction)
- {
- m_pasteAsSiblingAction->setEnabled(thereIsContentInTheClipboard);
- }
- if (m_pasteAsChildAction)
- {
- m_pasteAsChildAction->setEnabled(thereIsContentInTheClipboard && itemsAreSelected);
- }
- bool alignAllowed = ViewportAlign::IsAlignAllowed(this);
- for (QAction* action : m_actionsEnabledWithAlignAllowed)
- {
- action->setEnabled(alignAllowed);
- }
- GetModeToolbar()->GetAlignToolbarSection()->SetIsEnabled(alignAllowed);
- }
- void EditorWindow::RefreshEditorMenu()
- {
- m_actionsEnabledWithSelection.clear();
- m_pasteAsSiblingAction = nullptr;
- m_pasteAsChildAction = nullptr;
- m_actionsEnabledWithAlignAllowed.clear();
- auto actionList = actions();
- for (QAction* action : actionList)
- {
- removeAction(action);
- action->deleteLater();
- }
- menuBar()->clear();
- if (GetEditorMode() == UiEditorMode::Edit)
- {
- AddMenu_File();
- AddMenu_Edit();
- AddMenu_View();
- AddMenu_Preview();
- AddMenu_Help();
- }
- else
- {
- AddMenu_PreviewView();
- AddMenu_Preview();
- AddMenu_Help();
- }
- // Lastly, set up shortcuts that aren't on the menu since all actions were removed above
- SetupShortcuts();
- }
- void EditorWindow::SetupShortcuts()
- {
- // Actions with shortcuts are created instead of direct shortcuts because the shortcut dispatcher only looks for matching actions
- // Cycle coordinate system
- {
- QAction* action = new QAction("Coordinate System Cycle", this);
- action->setShortcut(QKeySequence(UICANVASEDITOR_COORDINATE_SYSTEM_CYCLE_SHORTCUT_KEY_SEQUENCE));
- QObject::connect(action,
- &QAction::triggered,
- [this]()
- {
- SignalCoordinateSystemCycle();
- });
- addAction(action);
- }
- // Toggle Snap to Grid
- {
- QAction* action = new QAction("Snap to Grid Toggle", this);
- action->setShortcut(QKeySequence(UICANVASEDITOR_SNAP_TO_GRID_TOGGLE_SHORTCUT_KEY_SEQUENCE));
- QObject::connect(action,
- &QAction::triggered,
- [this]()
- {
- SignalSnapToGridToggle();
- });
- addAction(action);
- }
- }
- void DisplayNullMetadataMessage(EditorWindow* editorWindow)
- {
- QMessageBox(QMessageBox::Critical,
- "Error",
- QString::fromUtf8("Unable to save: canvas metadata is null. Please try reopening the canvas."),
- QMessageBox::Ok, editorWindow).exec();
- }
- QAction* EditorWindow::CreateSaveCanvasAction(AZ::EntityId canvasEntityId, bool forContextMenu)
- {
- UiCanvasMetadata *canvasMetadata = canvasEntityId.IsValid() ? GetCanvasMetadata(canvasEntityId) : nullptr;
- AZStd::string canvasSourcePathname;
- AZStd::string canvasFilename;
- if (canvasMetadata)
- {
- canvasSourcePathname = canvasMetadata->m_canvasSourceAssetPathname;
- UiCanvasBus::EventResult(canvasFilename, canvasEntityId, &UiCanvasBus::Events::GetPathname);
- }
- QFileInfo fileInfo(canvasSourcePathname.c_str());
- QAction* action = new QAction(QString("&Save " + (fileInfo.fileName().isEmpty() ? "Canvas" : fileInfo.fileName())), this);
- if (!forContextMenu && !canvasFilename.empty())
- {
- action->setShortcut(QKeySequence::Save);
- }
- // If there's no filename,
- // we want the menu to be visible, but disabled.
- action->setEnabled(!canvasFilename.empty());
- QObject::connect(action,
- &QAction::triggered,
- this,
- [this, canvasEntityId]([[maybe_unused]] bool checked)
- {
- UiCanvasMetadata *canvasMetadata = GetCanvasMetadata(canvasEntityId);
- AZ_Assert(canvasMetadata, "Canvas metadata not found");
- if (canvasMetadata)
- {
- bool ok = SaveCanvasToXml(*canvasMetadata, false);
- if (!ok)
- {
- return;
- }
- // Refresh the File menu to update the
- // "Recent Files" and "Save".
- RefreshEditorMenu();
- }
- else
- {
- DisplayNullMetadataMessage(this);
- }
- });
- return action;
- }
- QAction* EditorWindow::CreateSaveCanvasAsAction(AZ::EntityId canvasEntityId, bool forContextMenu)
- {
- UiCanvasMetadata *canvasMetadata = canvasEntityId.IsValid() ? GetCanvasMetadata(canvasEntityId) : nullptr;
- AZStd::string canvasSourcePathname;
- AZStd::string canvasFilename;
- if (canvasMetadata)
- {
- canvasSourcePathname = canvasMetadata->m_canvasSourceAssetPathname;
- UiCanvasBus::EventResult(canvasFilename, canvasEntityId, &UiCanvasBus::Events::GetPathname);
- }
- QAction* action = new QAction("Save Canvas &As...", this);
- if (!forContextMenu && canvasFilename.empty())
- {
- action->setShortcut(QKeySequence::Save);
- }
- action->setEnabled(canvasMetadata);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [this, canvasEntityId]([[maybe_unused]] bool checked)
- {
- UiCanvasMetadata *canvasMetadata = GetCanvasMetadata(canvasEntityId);
- AZ_Assert(canvasMetadata, "Canvas metadata not found");
- if (canvasMetadata)
- {
- bool ok = SaveCanvasToXml(*canvasMetadata, true);
- if (!ok)
- {
- return;
- }
- // Refresh the File menu to update the
- // "Recent Files" and "Save".
- RefreshEditorMenu();
- }
- else
- {
- DisplayNullMetadataMessage(this);
- }
- });
- return action;
- }
- QAction* EditorWindow::CreateSaveSliceAction(UiCanvasMetadata *canvasMetadata, bool forContextMenu)
- {
- // We will never call this function unless canvasMetadata is non null and m_isSliceEditing is true
- AZ_Assert(canvasMetadata && canvasMetadata->m_isSliceEditing, "CreateSaveSliceAction requires valid canvas metadata and to be in slice editing mode");
- // as a safeguard check that the entity still exists
- AZ::EntityId sliceEntityId = canvasMetadata->m_sliceEntityId;
- AZ::Entity* sliceEntity = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(sliceEntity, &AZ::ComponentApplicationBus::Events::FindEntity, sliceEntityId);
- if (!sliceEntity)
- {
- // Slice entity not found, disable the menu item but also change it to indicate the error
- QAction* action = new QAction(QString("&Save Slice (slice entity not found)"), this);
- action->setEnabled(false);
- return action;
- }
- // get the slice address
- AZ::SliceComponent::SliceInstanceAddress sliceAddress;
- AzFramework::SliceEntityRequestBus::EventResult(sliceAddress, canvasMetadata->m_sliceEntityId,
- &AzFramework::SliceEntityRequestBus::Events::GetOwningSlice);
- // if isSliceEntity is false then something is wrong. The user could have done a detach slice for example
- if (!sliceAddress.IsValid() || !sliceAddress.GetReference()->GetSliceAsset())
- {
- // Slice entity is no longer a slice instance, disable the menu item but also change it to indicate the error
- QAction* action = new QAction(QString("&Save Slice (slice entity is no longer an instance)"), this);
- action->setEnabled(false);
- return action;
- }
- AZStd::string canvasDisplayName = canvasMetadata->m_canvasDisplayName;
- QAction* action = new QAction(QString("&Save ") + canvasDisplayName.c_str(), this);
- if (!forContextMenu)
- {
- action->setShortcut(QKeySequence::Save);
- action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
- }
- // There should always be a valid path for the slice but if there is not we disable the menu item.
- action->setEnabled(!canvasDisplayName.empty());
- QObject::connect(action,
- &QAction::triggered,
- [this, canvasMetadata]([[maybe_unused]] bool checked)
- {
- SaveSlice(*canvasMetadata);
- });
- return action;
- }
- QAction* EditorWindow::CreateSaveAllCanvasesAction([[maybe_unused]] bool forContextMenu)
- {
- QAction* action = new QAction(QString("Save All Canvases"), this);
- action->setEnabled(m_canvasMetadataMap.size() > 0);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [this]([[maybe_unused]] bool checked)
- {
- bool saved = false;
- for (auto mapItem : m_canvasMetadataMap)
- {
- auto canvasMetadata = mapItem.second;
- if (canvasMetadata->m_isSliceEditing)
- {
- saved |= SaveSlice(*canvasMetadata);
- }
- else
- {
- saved |= SaveCanvasToXml(*canvasMetadata, false);
- }
- }
- if (saved)
- {
- // Refresh the File menu to update the
- // "Recent Files" and "Save".
- RefreshEditorMenu();
- }
- });
- return action;
- }
- QAction* EditorWindow::CreateCloseCanvasAction(AZ::EntityId canvasEntityId, bool forContextMenu)
- {
- QAction* action = new QAction("&Close Canvas", this);
- if (!forContextMenu)
- {
- action->setShortcut(QKeySequence::Close);
- }
- action->setEnabled(canvasEntityId.IsValid());
- QObject::connect(action,
- &QAction::triggered,
- this,
- [this, canvasEntityId]([[maybe_unused]] bool checked)
- {
- CloseCanvas(canvasEntityId);
- });
- return action;
- }
- QAction* EditorWindow::CreateCloseAllOtherCanvasesAction(AZ::EntityId canvasEntityId, bool forContextMenu)
- {
- QAction* action = new QAction(forContextMenu ? "Close All but This Canvas" : "Close All but Active Canvas", this);
- action->setEnabled(m_canvasMetadataMap.size() > 1);
- QObject::connect(action,
- &QAction::triggered,
- this,
- [this, canvasEntityId]([[maybe_unused]] bool checked)
- {
- CloseAllOtherCanvases(canvasEntityId);
- });
- return action;
- }
- QAction* EditorWindow::CreateCloseAllCanvasesAction([[maybe_unused]] bool forContextMenu)
- {
- QAction* action = new QAction("Close All Canvases", this);
- action->setEnabled(m_canvasMetadataMap.size() > 0);
- QObject::connect(action,
- &QAction::triggered,
- this,
- &EditorWindow::CloseAllCanvases);
- return action;
- }
|