123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- /*
- * 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 "EditorDefs.h"
- #include "QtEditorApplication.h"
- // Qt
- #include <QAbstractEventDispatcher>
- #include <QScopedValueRollback>
- #include <QToolBar>
- #include <QLoggingCategory>
- #include <AzCore/Component/ComponentApplication.h>
- #include <AzCore/IO/Path/Path.h>
- #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
- // AzQtComponents
- #include <AzQtComponents/Components/GlobalEventFilter.h>
- #include <AzQtComponents/Components/O3DEStylesheet.h>
- #include <AzQtComponents/Components/Titlebar.h>
- #include <AzQtComponents/Components/WindowDecorationWrapper.h>
- // Editor
- #include "Settings.h"
- #include "CryEdit.h"
- Q_LOGGING_CATEGORY(InputDebugging, "o3de.editor.input")
- // internal, private namespace:
- namespace
- {
- class EditorGlobalEventFilter
- : public AzQtComponents::GlobalEventFilter
- {
- public:
- explicit EditorGlobalEventFilter(QObject* watch)
- : AzQtComponents::GlobalEventFilter(watch) {}
- bool eventFilter(QObject* obj, QEvent* e) override
- {
- static bool isRecursing = false;
- if (isRecursing)
- {
- return false;
- }
- QScopedValueRollback<bool> guard(isRecursing, true);
- // Detect Widget move
- // We're doing this before the events are actually consumed to avoid confusion
- if (IsDragGuardedWidget(obj))
- {
- switch (e->type())
- {
- case QEvent::MouseButtonPress:
- {
- m_widgetDraggedState = WidgetDraggedState::Clicked;
- break;
- }
- case QEvent::Move:
- case QEvent::MouseMove:
- {
- if (m_widgetDraggedState == WidgetDraggedState::Clicked)
- {
- m_widgetDraggedState = WidgetDraggedState::Dragged;
- }
- break;
- }
- }
- }
- if (e->type() == QEvent::MouseButtonRelease)
- {
- m_widgetDraggedState = WidgetDraggedState::None;
- }
- switch (e->type())
- {
- case QEvent::KeyPress:
- case QEvent::KeyRelease:
- {
- if (GetIEditor()->IsInGameMode())
- {
- // don't let certain keys fall through to the game when it's running
- QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e);
- auto key = keyEvent->key();
- if ((key == Qt::Key_Alt) || (key == Qt::Key_AltGr) || ((key >= Qt::Key_F1) && (key <= Qt::Key_F35)))
- {
- return true;
- }
- }
- }
- break;
- case QEvent::Shortcut:
- {
- // Eat shortcuts in game mode or when a guarded widget is being dragged
- if (GetIEditor()->IsInGameMode() || m_widgetDraggedState == WidgetDraggedState::Dragged)
- {
- return true;
- }
- }
- break;
- case QEvent::MouseButtonPress:
- case QEvent::MouseButtonRelease:
- case QEvent::MouseButtonDblClick:
- case QEvent::MouseMove:
- {
- #if AZ_TRAIT_OS_PLATFORM_APPLE
- auto widget = qobject_cast<QWidget*>(obj);
- if (widget && widget->graphicsProxyWidget() != nullptr)
- {
- QMouseEvent* me = static_cast<QMouseEvent*>(e);
- QWidget* target = qApp->widgetAt(QCursor::pos());
- if (target)
- {
- QMouseEvent ev(me->type(), target->mapFromGlobal(QCursor::pos()), me->button(), me->buttons(), me->modifiers());
- qApp->notify(target, &ev);
- return true;
- }
- }
- #endif
- GuardMouseEventSelectionChangeMetrics(e);
- }
- break;
- }
- return GlobalEventFilter::eventFilter(obj, e);
- }
- private:
- bool m_mouseButtonWasDown = false;
- void GuardMouseEventSelectionChangeMetrics(QEvent* e)
- {
- // Force the metrics collector to queue up any selection changed metrics until mouse release, so that we don't
- // get flooded with multiple selection changed events when one, sent on mouse release, is enough.
- if (e->type() == QEvent::MouseButtonPress)
- {
- if (!m_mouseButtonWasDown)
- {
- m_mouseButtonWasDown = true;
- }
- }
- else if (e->type() == QEvent::MouseButtonRelease)
- {
- // This is a tricky case. We don't want to send the end selection change event too early
- // because there might be other things responding to the mouse release after this, and we want to
- // block handling of the selection change events until we're entirely finished with the mouse press.
- // So, queue the handling with a single shot timer, but then check the state of the mouse buttons
- // to ensure that they haven't been pressed in between the release and the timer firing off.
- QTimer::singleShot(0, this, [this]() {
- if (!QApplication::mouseButtons() && m_mouseButtonWasDown)
- {
- m_mouseButtonWasDown = false;
- }
- });
- }
- }
- //! Detect if the event's target is a Widget we want to guard from shortcuts while it's being dragged.
- //! This function can be easily expanded to handle exceptions.
- bool IsDragGuardedWidget(const QObject* obj)
- {
- return qobject_cast<const QWidget*>(obj) != nullptr;
- }
- //! Enum to keep track of Widget dragged state
- enum class WidgetDraggedState
- {
- None, //!< No widget is being clicked nor dragged
- Clicked, //!< A widget has been clicked on but has not been dragged
- Dragged, //!< A widget is being dragged
- };
- WidgetDraggedState m_widgetDraggedState = WidgetDraggedState::None;
- };
- static void LogToDebug([[maybe_unused]] QtMsgType Type, [[maybe_unused]] const QMessageLogContext& Context, const QString& message)
- {
- AZ::Debug::Platform::OutputToDebugger("Qt", message.toUtf8().data());
- AZ::Debug::Platform::OutputToDebugger("", "\n");
- }
- }
- namespace Editor
- {
- void ScanDirectories(QFileInfoList& directoryList, const QStringList& filters, QFileInfoList& files, ScanDirectoriesUpdateCallBack updateCallback)
- {
- while (!directoryList.isEmpty())
- {
- QDir directory(directoryList.front().absoluteFilePath(), "*", QDir::Name | QDir::IgnoreCase, QDir::AllEntries);
- directoryList.pop_front();
- if (directory.exists())
- {
- // Append each file from this directory that matches one of the filters to files
- directory.setNameFilters(filters);
- directory.setFilter(QDir::Files);
- files.append(directory.entryInfoList());
- // Add all of the subdirectories from this directory to the queue to be searched
- directory.setNameFilters(QStringList("*"));
- directory.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
- directoryList.append(directory.entryInfoList());
- if (updateCallback)
- {
- updateCallback();
- }
- }
- }
- }
- EditorQtApplication::EditorQtApplication(int& argc, char** argv)
- : AzQtApplication(argc, argv)
- , m_stylesheet(new AzQtComponents::O3DEStylesheet(this))
- {
- setWindowIcon(QIcon(":/Application/res/o3de_editor.ico"));
- // set the default key store for our preferences:
- setApplicationName("O3DE Editor");
- installEventFilter(this);
- // Disable our debugging input helpers by default
- QLoggingCategory::setFilterRules(QStringLiteral("o3de.editor.input.*=false"));
- // Initialize our stylesheet here to allow Gems to register stylesheets when their system components activate.
- AZ::IO::FixedMaxPath engineRootPath;
- {
- using namespace AZ::SettingsRegistryMergeUtils;
- AZ::SettingsRegistryImpl settingsRegistry;
- AZ::CommandLine commandLine;
- commandLine.Parse(argc, argv);
- ParseCommandLine(commandLine);
- StoreCommandLineToRegistry(settingsRegistry, commandLine);
- MergeSettingsToRegistry_CommandLine(settingsRegistry, commandLine, {});
- MergeSettingsToRegistry_AddRuntimeFilePaths(settingsRegistry);
- settingsRegistry.Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
- }
- m_stylesheet->initialize(this, engineRootPath);
- }
- void EditorQtApplication::Initialize()
- {
- GetIEditor()->RegisterNotifyListener(this);
- // install QTranslator
- InstallEditorTranslators();
- // install hooks and filters last and revoke them first
- InstallFilters();
- // install this filter. It will be a parent of the application and cleaned up when it is cleaned up automically
- auto globalEventFilter = new EditorGlobalEventFilter(this);
- installEventFilter(globalEventFilter);
- }
- void EditorQtApplication::LoadSettings()
- {
- AZ::SerializeContext* context;
- AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
- AZ_Assert(context, "No serialize context");
- char resolvedPath[AZ_MAX_PATH_LEN];
- AZ::IO::FileIOBase::GetInstance()->ResolvePath("@user@/EditorUserSettings.xml", resolvedPath, AZ_MAX_PATH_LEN);
- m_localUserSettings.Load(resolvedPath, context);
- m_localUserSettings.Activate(AZ::UserSettings::CT_LOCAL);
- AZ::UserSettingsOwnerRequestBus::Handler::BusConnect(AZ::UserSettings::CT_LOCAL);
- m_activatedLocalUserSettings = true;
- }
- void EditorQtApplication::UnloadSettings()
- {
- if (m_activatedLocalUserSettings)
- {
- SaveSettings();
- m_localUserSettings.Deactivate();
- AZ::UserSettingsOwnerRequestBus::Handler::BusDisconnect();
- m_activatedLocalUserSettings = false;
- }
- }
- void EditorQtApplication::SaveSettings()
- {
- if (m_activatedLocalUserSettings)
- {
- AZ::SerializeContext* context;
- AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
- AZ_Assert(context, "No serialize context");
- char resolvedPath[AZ_MAX_PATH_LEN];
- AZ::IO::FileIOBase::GetInstance()->ResolvePath("@user@/EditorUserSettings.xml", resolvedPath, AZ_ARRAY_SIZE(resolvedPath));
- m_localUserSettings.Save(resolvedPath, context);
- }
- }
- void EditorQtApplication::maybeProcessIdle()
- {
- if (!m_isMovingOrResizing)
- {
- if (auto winapp = CCryEditApp::instance())
- {
- winapp->OnIdle(0);
- }
- }
- if (m_applicationActive)
- {
- QTimer::singleShot(1, this, &EditorQtApplication::maybeProcessIdle);
- }
- }
- void EditorQtApplication::InstallQtLogHandler()
- {
- qInstallMessageHandler(LogToDebug);
- }
- void EditorQtApplication::InstallFilters()
- {
- if (auto dispatcher = QAbstractEventDispatcher::instance())
- {
- dispatcher->installNativeEventFilter(this);
- }
- }
- void EditorQtApplication::UninstallFilters()
- {
- if (auto dispatcher = QAbstractEventDispatcher::instance())
- {
- dispatcher->removeNativeEventFilter(this);
- }
- }
- EditorQtApplication::~EditorQtApplication()
- {
- if (GetIEditor())
- {
- GetIEditor()->UnregisterNotifyListener(this);
- }
- UninstallFilters();
- UninstallEditorTranslators();
- }
- EditorQtApplication* EditorQtApplication::instance()
- {
- return static_cast<EditorQtApplication*>(QApplication::instance());
- }
- void EditorQtApplication::OnEditorNotifyEvent(EEditorNotifyEvent event)
- {
- switch (event)
- {
- case eNotify_OnStyleChanged:
- RefreshStyleSheet();
- emit skinChanged();
- break;
- case eNotify_OnQuit:
- GetIEditor()->UnregisterNotifyListener(this);
- break;
- }
- }
- QColor EditorQtApplication::InterpolateColors(QColor a, QColor b, float factor)
- {
- return QColor(int(a.red() * (1.0f - factor) + b.red() * factor),
- int(a.green() * (1.0f - factor) + b.green() * factor),
- int(a.blue() * (1.0f - factor) + b.blue() * factor),
- int(a.alpha() * (1.0f - factor) + b.alpha() * factor));
- }
- void EditorQtApplication::RefreshStyleSheet()
- {
- m_stylesheet->Refresh();
- }
- void EditorQtApplication::setIsMovingOrResizing(bool isMovingOrResizing)
- {
- if (m_isMovingOrResizing == isMovingOrResizing)
- {
- return;
- }
- m_isMovingOrResizing = isMovingOrResizing;
- }
- bool EditorQtApplication::isMovingOrResizing() const
- {
- return m_isMovingOrResizing;
- }
- const QColor& EditorQtApplication::GetColorByName(const QString& name)
- {
- return m_stylesheet->GetColorByName(name);
- }
- bool EditorQtApplication::IsActive()
- {
- return applicationState() == Qt::ApplicationActive;
- }
- QTranslator* EditorQtApplication::CreateAndInitializeTranslator(const QString& filename, const QString& directory)
- {
- Q_ASSERT(QFile::exists(directory + "/" + filename));
- QTranslator* translator = new QTranslator();
- translator->load(filename, directory);
- installTranslator(translator);
- return translator;
- }
- void EditorQtApplication::InstallEditorTranslators()
- {
- m_editorTranslator = CreateAndInitializeTranslator("editor_en-us.qm", ":/Translations");
- m_assetBrowserTranslator = CreateAndInitializeTranslator("assetbrowser_en-us.qm", ":/Translations");
- }
- void EditorQtApplication::DeleteTranslator(QTranslator*& translator)
- {
- removeTranslator(translator);
- delete translator;
- translator = nullptr;
- }
- void EditorQtApplication::UninstallEditorTranslators()
- {
- DeleteTranslator(m_editorTranslator);
- DeleteTranslator(m_assetBrowserTranslator);
- }
- void EditorQtApplication::EnableOnIdle(bool enable)
- {
- m_applicationActive = enable;
- if (enable)
- {
- QTimer::singleShot(0, this, &EditorQtApplication::maybeProcessIdle);
- }
- }
- bool EditorQtApplication::OnIdleEnabled() const
- {
- return m_applicationActive;
- }
- bool EditorQtApplication::eventFilter(QObject* object, QEvent* event)
- {
- switch (event->type())
- {
- case QEvent::MouseButtonPress:
- m_pressedButtons |= reinterpret_cast<QMouseEvent*>(event)->button();
- break;
- case QEvent::MouseButtonRelease:
- m_pressedButtons &= ~(reinterpret_cast<QMouseEvent*>(event)->button());
- break;
- case QEvent::KeyPress:
- m_pressedKeys.insert(reinterpret_cast<QKeyEvent*>(event)->key());
- break;
- case QEvent::KeyRelease:
- m_pressedKeys.remove(reinterpret_cast<QKeyEvent*>(event)->key());
- break;
- default:
- break;
- }
- return QApplication::eventFilter(object, event);
- }
- } // end namespace Editor
- #include <Core/moc_QtEditorApplication.cpp>
|