Resolved merge conflicts
Signed-off-by: Chris Galvan <chgalvan@amazon.com>
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 "EditorMetricsPlainTextNameRegistration.h"
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
|
||||
EditorMetricsPlainTextNameRegistrationBusListener::EditorMetricsPlainTextNameRegistrationBusListener()
|
||||
{
|
||||
AzFramework::MetricsPlainTextNameRegistrationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
EditorMetricsPlainTextNameRegistrationBusListener::~EditorMetricsPlainTextNameRegistrationBusListener()
|
||||
{
|
||||
AzFramework::MetricsPlainTextNameRegistrationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void EditorMetricsPlainTextNameRegistrationBusListener::RegisterForNameSending(const AZStd::vector<AZ::Uuid>& typeIdsThatCanBeSentAsPlainText)
|
||||
{
|
||||
m_registeredTypeIds.insert(typeIdsThatCanBeSentAsPlainText.begin(), typeIdsThatCanBeSentAsPlainText.end());
|
||||
}
|
||||
|
||||
bool EditorMetricsPlainTextNameRegistrationBusListener::IsTypeRegisteredForNameSending(const AZ::Uuid& typeId)
|
||||
{
|
||||
return (m_registeredTypeIds.find(typeId) != m_registeredTypeIds.end());
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzFramework/Metrics/MetricsPlainTextNameRegistration.h>
|
||||
#include <AzCore/std/containers/set.h>
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
class EditorMetricsPlainTextNameRegistrationBusListener : public AzFramework::MetricsPlainTextNameRegistrationBus::Handler
|
||||
{
|
||||
public:
|
||||
EditorMetricsPlainTextNameRegistrationBusListener();
|
||||
virtual ~EditorMetricsPlainTextNameRegistrationBusListener();
|
||||
|
||||
void RegisterForNameSending(const AZStd::vector<AZ::Uuid>& typeIdsThatCanBeSentAsPlainText) override;
|
||||
|
||||
bool IsTypeRegisteredForNameSending(const AZ::Uuid& typeId) override;
|
||||
|
||||
private:
|
||||
AZStd::set<AZ::Uuid> m_registeredTypeIds;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef LEVELEDITORMENUHANDLER_H
|
||||
#define LEVELEDITORMENUHANDLER_H
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QMenu>
|
||||
#include <QPointer>
|
||||
#include "ActionManager.h"
|
||||
#include "QtViewPaneManager.h"
|
||||
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
|
||||
#endif
|
||||
|
||||
class MainWindow;
|
||||
class QtViewPaneManager;
|
||||
class QSettings;
|
||||
struct QtViewPane;
|
||||
|
||||
class LevelEditorMenuHandler
|
||||
: public QObject
|
||||
, private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
|
||||
, private AzToolsFramework::EditorMenuRequestBus::Handler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LevelEditorMenuHandler(MainWindow* mainWindow, QtViewPaneManager* const viewPaneManager, QSettings& settings);
|
||||
~LevelEditorMenuHandler();
|
||||
|
||||
void Initialize();
|
||||
|
||||
bool MRUEntryIsValid(const QString& entry, const QString& gameFolderPath);
|
||||
|
||||
void IncrementViewPaneVersion();
|
||||
int GetViewPaneVersion() const;
|
||||
|
||||
void UpdateViewLayoutsMenu(ActionManager::MenuWrapper& layoutsMenu);
|
||||
|
||||
void ResetToolsMenus();
|
||||
|
||||
QAction* CreateViewPaneAction(const QtViewPane* view);
|
||||
|
||||
// It's used when users update the Tool Box Macro list in the Configure Tool Box Macro dialog
|
||||
void UpdateMacrosMenu();
|
||||
|
||||
Q_SIGNALS:
|
||||
void ActivateAssetImporter();
|
||||
|
||||
private:
|
||||
QMenu* CreateFileMenu();
|
||||
QMenu* CreateEditMenu();
|
||||
void PopulateEditMenu(ActionManager::MenuWrapper& editMenu);
|
||||
QMenu* CreateGameMenu();
|
||||
QMenu* CreateToolsMenu();
|
||||
QMenu* CreateViewMenu();
|
||||
QMenu* CreateHelpMenu();
|
||||
|
||||
void checkOrOpenView();
|
||||
|
||||
QMap<QString, QList<QtViewPane*>> CreateMenuMap(QMap<QString, QList<QtViewPane*>>& menuMap, QtViewPanes& allRegisteredViewPanes);
|
||||
void CreateMenuOptions(QMap<QString, QList<QtViewPane*>>* menuMap, ActionManager::MenuWrapper& menu, const char* category);
|
||||
|
||||
void CreateDebuggingSubMenu(ActionManager::MenuWrapper gameMenu);
|
||||
|
||||
void UpdateMRUFiles();
|
||||
|
||||
void ClearAll();
|
||||
void OnUpdateOpenRecent();
|
||||
|
||||
void OnUpdateMacrosMenu();
|
||||
|
||||
void UpdateOpenViewPaneMenu();
|
||||
|
||||
QAction* CreateViewPaneMenuItem(ActionManager* actionManager, ActionManager::MenuWrapper& menu, const QtViewPane* view);
|
||||
|
||||
void InitializeViewPaneMenu(ActionManager* actionManager, ActionManager::MenuWrapper& menu, AZStd::function < bool(const QtViewPane& view)> functor);
|
||||
|
||||
void LoadComponentLayout();
|
||||
|
||||
void AddDisableActionInSimModeListener(QAction* action);
|
||||
|
||||
// EditorComponentModeNotificationBus
|
||||
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
|
||||
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
|
||||
|
||||
// EditorMenuRequestBus
|
||||
void AddEditMenuAction(QAction* action) override;
|
||||
void AddMenuAction(AZStd::string_view categoryId, QAction* action, bool addToToolsToolbar) override;
|
||||
void RestoreEditMenuToDefault() override;
|
||||
|
||||
MainWindow* m_mainWindow;
|
||||
ActionManager* m_actionManager;
|
||||
QtViewPaneManager* m_viewPaneManager;
|
||||
|
||||
QPointer<QMenu> m_viewportViewsMenu;
|
||||
|
||||
ActionManager::MenuWrapper m_toolsMenu;
|
||||
|
||||
QMenu* m_mostRecentLevelsMenu = nullptr;
|
||||
QMenu* m_mostRecentProjectsMenu = nullptr;
|
||||
QMenu* m_editmenu = nullptr;
|
||||
|
||||
ActionManager::MenuWrapper m_viewPanesMenu;
|
||||
ActionManager::MenuWrapper m_layoutsMenu;
|
||||
ActionManager::MenuWrapper m_macrosMenu;
|
||||
|
||||
const char* m_levelExtension = nullptr;
|
||||
int m_viewPaneVersion = 0;
|
||||
|
||||
QList<QMenu*> m_topLevelMenus;
|
||||
QSettings& m_settings;
|
||||
};
|
||||
|
||||
#endif // LEVELEDITORMENUHANDLER_H
|
||||
@@ -0,0 +1,659 @@
|
||||
|
||||
/*
|
||||
* 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>
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
#include <QtGui/qpa/qplatformnativeinterface.h>
|
||||
#include <QtGui/private/qhighdpiscaling_p.h>
|
||||
#endif
|
||||
|
||||
#include <AzCore/Component/ComponentApplication.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
// AzFramework
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
# include <AzFramework/Input/Buses/Notifications/RawInputNotificationBus_Platform.h>
|
||||
#endif // defined(AZ_PLATFORM_WINDOWS)
|
||||
|
||||
// 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"
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
// in milliseconds
|
||||
GameModeIdleFrequency = 0,
|
||||
EditorModeIdleFrequency = 1,
|
||||
InactiveModeFrequency = 10,
|
||||
UninitializedFrequency = 9999,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
OutputDebugStringW(L"Qt: ");
|
||||
OutputDebugStringW(reinterpret_cast<const wchar_t*>(message.utf16()));
|
||||
OutputDebugStringW(L"\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
: QApplication(argc, argv)
|
||||
, m_inWinEventFilter(false)
|
||||
, m_stylesheet(new AzQtComponents::O3DEStylesheet(this))
|
||||
, m_idleTimer(new QTimer(this))
|
||||
{
|
||||
m_idleTimer->setInterval(UninitializedFrequency);
|
||||
|
||||
setWindowIcon(QIcon(":/Application/res/o3de_editor.ico"));
|
||||
|
||||
// set the default key store for our preferences:
|
||||
setOrganizationName("O3DE");
|
||||
setOrganizationDomain("o3de.org");
|
||||
setApplicationName("O3DE Editor");
|
||||
|
||||
connect(m_idleTimer, &QTimer::timeout, this, &EditorQtApplication::maybeProcessIdle);
|
||||
|
||||
connect(this, &QGuiApplication::applicationStateChanged, this, [this] { ResetIdleTimerInterval(PollState); });
|
||||
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;
|
||||
{
|
||||
// Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry
|
||||
AZ::ComponentApplication application(argc, argv);
|
||||
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
||||
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;
|
||||
EBUS_EVENT_RESULT(context, AZ::ComponentApplicationBus, 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;
|
||||
EBUS_EVENT_RESULT(context, AZ::ComponentApplicationBus, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
#if defined(AZ_PLATFORM_WINDOWS)
|
||||
bool EditorQtApplication::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long* result)
|
||||
{
|
||||
MSG* msg = (MSG*)message;
|
||||
|
||||
if (msg->message == WM_MOVING || msg->message == WM_SIZING)
|
||||
{
|
||||
m_isMovingOrResizing = true;
|
||||
}
|
||||
else if (msg->message == WM_EXITSIZEMOVE)
|
||||
{
|
||||
m_isMovingOrResizing = false;
|
||||
}
|
||||
|
||||
// Prevent the user from being able to move the window in game mode.
|
||||
// This is done during the hit test phase to bypass the native window move messages. If the window
|
||||
// decoration wrapper title bar contains the cursor, set the result to HTCLIENT instead of
|
||||
// HTCAPTION.
|
||||
if (msg->message == WM_NCHITTEST && GetIEditor()->IsInGameMode())
|
||||
{
|
||||
const LRESULT defWinProcResult = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
|
||||
if (defWinProcResult == 1)
|
||||
{
|
||||
if (QWidget* widget = QWidget::find((WId)msg->hwnd))
|
||||
{
|
||||
if (auto wrapper = qobject_cast<const AzQtComponents::WindowDecorationWrapper *>(widget))
|
||||
{
|
||||
AzQtComponents::TitleBar* titleBar = wrapper->titleBar();
|
||||
const short global_x = static_cast<short>(LOWORD(msg->lParam));
|
||||
const short global_y = static_cast<short>(HIWORD(msg->lParam));
|
||||
|
||||
const QPoint globalPos = QHighDpi::fromNativePixels(QPoint(global_x, global_y), widget->window()->windowHandle());
|
||||
const QPoint local = titleBar->mapFromGlobal(globalPos);
|
||||
if (titleBar->draggableRect().contains(local) && !titleBar->isTopResizeArea(globalPos))
|
||||
{
|
||||
*result = HTCLIENT;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the Windows WM_INPUT messages get passed through to the AzFramework input system.
|
||||
// These events are now consumed both in and out of game mode.
|
||||
if (msg->message == WM_INPUT)
|
||||
{
|
||||
UINT rawInputSize;
|
||||
const UINT rawInputHeaderSize = sizeof(RAWINPUTHEADER);
|
||||
GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, NULL, &rawInputSize, rawInputHeaderSize);
|
||||
|
||||
AZStd::array<BYTE, sizeof(RAWINPUT)> rawInputBytesArray;
|
||||
LPBYTE rawInputBytes = rawInputBytesArray.data();
|
||||
|
||||
const UINT bytesCopied = GetRawInputData((HRAWINPUT)msg->lParam, RID_INPUT, rawInputBytes, &rawInputSize, rawInputHeaderSize);
|
||||
CRY_ASSERT(bytesCopied == rawInputSize);
|
||||
|
||||
RAWINPUT* rawInput = (RAWINPUT*)rawInputBytes;
|
||||
CRY_ASSERT(rawInput);
|
||||
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(&AzFramework::RawInputNotificationsWindows::OnRawInputEvent, *rawInput);
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (msg->message == WM_DEVICECHANGE)
|
||||
{
|
||||
if (msg->wParam == 0x0007) // DBT_DEVNODES_CHANGED
|
||||
{
|
||||
AzFramework::RawInputNotificationBusWindows::Broadcast(&AzFramework::RawInputNotificationsWindows::OnRawInputDeviceChangeEvent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void EditorQtApplication::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case eNotify_OnStyleChanged:
|
||||
RefreshStyleSheet();
|
||||
emit skinChanged();
|
||||
break;
|
||||
|
||||
case eNotify_OnQuit:
|
||||
GetIEditor()->UnregisterNotifyListener(this);
|
||||
break;
|
||||
|
||||
case eNotify_OnBeginGameMode:
|
||||
// GetIEditor()->IsInGameMode() Isn't reliable when called from within the notification handler
|
||||
ResetIdleTimerInterval(GameMode);
|
||||
break;
|
||||
case eNotify_OnEndGameMode:
|
||||
ResetIdleTimerInterval(EditorMode);
|
||||
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);
|
||||
}
|
||||
|
||||
EditorQtApplication* EditorQtApplication::instance()
|
||||
{
|
||||
return static_cast<EditorQtApplication*>(QApplication::instance());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
if (m_idleTimer->interval() == UninitializedFrequency)
|
||||
{
|
||||
ResetIdleTimerInterval();
|
||||
}
|
||||
|
||||
m_idleTimer->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_idleTimer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorQtApplication::OnIdleEnabled() const
|
||||
{
|
||||
if (m_idleTimer->interval() == UninitializedFrequency)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_idleTimer->isActive();
|
||||
}
|
||||
|
||||
void EditorQtApplication::ResetIdleTimerInterval(TimerResetFlag flag)
|
||||
{
|
||||
bool isInGameMode = flag == GameMode;
|
||||
if (flag == PollState)
|
||||
{
|
||||
isInGameMode = GetIEditor() ? GetIEditor()->IsInGameMode() : false;
|
||||
}
|
||||
|
||||
// Game mode takes precedence over anything else
|
||||
if (isInGameMode)
|
||||
{
|
||||
m_idleTimer->setInterval(GameModeIdleFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (applicationState() & Qt::ApplicationActive)
|
||||
{
|
||||
m_idleTimer->setInterval(EditorModeIdleFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_idleTimer->setInterval(InactiveModeFrequency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
#ifdef AZ_PLATFORM_WINDOWS
|
||||
case QEvent::Leave:
|
||||
{
|
||||
// if we receive a leave event for a toolbar on Windows
|
||||
// check first whether we really left it. If we didn't: start checking
|
||||
// for the tool bar under the mouse by timer to check when we really left.
|
||||
// Synthesize a new leave event then. Workaround for LY-69788
|
||||
auto toolBarAt = [](const QPoint& pos) -> QToolBar* {
|
||||
QWidget* widget = qApp->widgetAt(pos);
|
||||
while (widget != nullptr)
|
||||
{
|
||||
if (QToolBar* tb = qobject_cast<QToolBar*>(widget))
|
||||
{
|
||||
return tb;
|
||||
}
|
||||
widget = widget->parentWidget();
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
if (object == toolBarAt(QCursor::pos()))
|
||||
{
|
||||
QTimer* t = new QTimer(object);
|
||||
t->start(100);
|
||||
connect(t, &QTimer::timeout, object, [t, object, toolBarAt]() {
|
||||
if (object != toolBarAt(QCursor::pos()))
|
||||
{
|
||||
QEvent event(QEvent::Leave);
|
||||
qApp->sendEvent(object, &event);
|
||||
t->deleteLater();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QApplication::eventFilter(object, event);
|
||||
}
|
||||
} // end namespace Editor
|
||||
|
||||
#include <Core/moc_QtEditorApplication.cpp>
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QApplication>
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QColor>
|
||||
#include <QMap>
|
||||
#include <QTranslator>
|
||||
#include <QSet>
|
||||
#include "IEventLoopHook.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#include <AzCore/PlatformDef.h>
|
||||
#include <AzCore/UserSettings/UserSettingsProvider.h>
|
||||
#include <IEditor.h>
|
||||
#endif
|
||||
|
||||
class QFileInfo;
|
||||
typedef QList<QFileInfo> QFileInfoList;
|
||||
class QByteArray;
|
||||
|
||||
namespace AzQtComponents
|
||||
{
|
||||
class O3DEStylesheet;
|
||||
}
|
||||
|
||||
enum EEditorNotifyEvent;
|
||||
|
||||
// This contains the main "QApplication"-derived class for the editor itself.
|
||||
// it performs the message hooking and other functions to allow CryEdit to function.
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
typedef void(* ScanDirectoriesUpdateCallBack)(void);
|
||||
/*!
|
||||
\param directoryList A list of directories to search. ScanDirectories will also search the subdirectories of each of these.
|
||||
\param filters A list of filename filters. Supports * and ? wildcards. The filters will not apply to the directory names.
|
||||
\param files Any file that is found and matches any of the filters will be added to files.
|
||||
\param updateCallback An optional callback function that will be called once for every directory and subdirectory that is scanned.
|
||||
*/
|
||||
void ScanDirectories(QFileInfoList& directoryList, const QStringList& filters, QFileInfoList& files, ScanDirectoriesUpdateCallBack updateCallback = nullptr);
|
||||
|
||||
class EditorQtApplication
|
||||
: public QApplication
|
||||
, public QAbstractNativeEventFilter
|
||||
, public IEditorNotifyListener
|
||||
, public AZ::UserSettingsOwnerRequestBus::Handler
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QSet<int> pressedKeys READ pressedKeys)
|
||||
Q_PROPERTY(int pressedMouseButtons READ pressedMouseButtons)
|
||||
public:
|
||||
// Call this before creating this object:
|
||||
static void InstallQtLogHandler();
|
||||
|
||||
EditorQtApplication(int& argc, char** argv);
|
||||
virtual ~EditorQtApplication();
|
||||
void Initialize();
|
||||
|
||||
void LoadSettings();
|
||||
void UnloadSettings();
|
||||
|
||||
// AZ::UserSettingsOwnerRequestBus::Handler
|
||||
void SaveSettings() override;
|
||||
////
|
||||
|
||||
static EditorQtApplication* instance();
|
||||
|
||||
static bool IsActive();
|
||||
|
||||
bool isMovingOrResizing() const;
|
||||
|
||||
// QAbstractNativeEventFilter:
|
||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override;
|
||||
|
||||
// IEditorNotifyListener:
|
||||
void OnEditorNotifyEvent(EEditorNotifyEvent event) override;
|
||||
|
||||
const QColor& GetColorByName(const QString& colorName);
|
||||
|
||||
void EnableOnIdle(bool enable = true);
|
||||
bool OnIdleEnabled() const;
|
||||
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
QSet<int> pressedKeys() const { return m_pressedKeys; }
|
||||
int pressedMouseButtons() const { return m_pressedButtons; }
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
void setIsMovingOrResizing(bool isMovingOrResizing);
|
||||
|
||||
signals:
|
||||
void skinChanged();
|
||||
|
||||
private:
|
||||
enum TimerResetFlag
|
||||
{
|
||||
PollState,
|
||||
GameMode,
|
||||
EditorMode
|
||||
};
|
||||
void ResetIdleTimerInterval(TimerResetFlag = PollState);
|
||||
static QColor InterpolateColors(QColor a, QColor b, float factor);
|
||||
void RefreshStyleSheet();
|
||||
void InstallFilters();
|
||||
void UninstallFilters();
|
||||
void maybeProcessIdle();
|
||||
|
||||
AzQtComponents::O3DEStylesheet* m_stylesheet;
|
||||
|
||||
bool m_inWinEventFilter = false;
|
||||
|
||||
// Translators
|
||||
void InstallEditorTranslators();
|
||||
void UninstallEditorTranslators();
|
||||
QTranslator* CreateAndInitializeTranslator(const QString& filename, const QString& directory);
|
||||
void DeleteTranslator(QTranslator*& translator);
|
||||
|
||||
QTranslator* m_editorTranslator = nullptr;
|
||||
QTranslator* m_assetBrowserTranslator = nullptr;
|
||||
QTimer* const m_idleTimer = nullptr;
|
||||
bool m_isMovingOrResizing = false;
|
||||
|
||||
AZ::UserSettingsProvider m_localUserSettings;
|
||||
|
||||
Qt::MouseButtons m_pressedButtons = Qt::NoButton;
|
||||
QSet<int> m_pressedKeys;
|
||||
|
||||
bool m_activatedLocalUserSettings = false;
|
||||
};
|
||||
} // namespace editor
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 "QtEditorApplication.h"
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
bool EditorQtApplication::nativeEventFilter(const QByteArray& , void* , long* )
|
||||
{
|
||||
// TODO_KDAB_LINUX
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
#import <AppKit/NSEvent.h>
|
||||
|
||||
#include "EditorDefs.h"
|
||||
#include "QtEditorApplication.h"
|
||||
|
||||
// AzFramework
|
||||
#include <AzFramework/Input/Buses/Notifications/RawInputNotificationBus_Platform.h>
|
||||
|
||||
// Qt
|
||||
#include <QCursor>
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
bool EditorQtApplication::nativeEventFilter(const QByteArray& eventType, void* message, long* result)
|
||||
{
|
||||
NSEvent* event = (NSEvent*)message;
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
AzFramework::RawInputNotificationBusMac::Broadcast(&AzFramework::RawInputNotificationsMac::OnRawInputEvent, event);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ([event type] == NSEventTypeMouseEntered)
|
||||
{
|
||||
// filter out mouse enter events when the widget the mouse entered into
|
||||
// does not exist anymore. This happened occasionally on startup when hovering
|
||||
// over the splash screen - LY-84100
|
||||
if (widgetAt(QCursor::pos()) == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // end namespace Editor
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
// AzTest
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
// Qt
|
||||
#include <QBuffer>
|
||||
|
||||
static QString CreateTestString(int size)
|
||||
{
|
||||
QString testString;
|
||||
testString.resize(size);
|
||||
|
||||
// generate a string with some kind of pattern, so that
|
||||
// decoding is verified against something semi-realistic
|
||||
QString pattern = "TestPattern";
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
testString[i] = pattern[i % pattern.length()];
|
||||
}
|
||||
|
||||
return testString;
|
||||
}
|
||||
|
||||
TEST(CArchive, 1ByteLengthString)
|
||||
{
|
||||
QString outString = CreateTestString(20); // use a test size less than 2^8, so it takes only 1 byte for the length
|
||||
|
||||
QBuffer memBlock;
|
||||
memBlock.open(QIODevice::ReadWrite);
|
||||
CArchive writeArchive(&memBlock, CArchive::store);
|
||||
writeArchive << outString;
|
||||
|
||||
QByteArray dataBuffer = memBlock.buffer();
|
||||
|
||||
// buffer size after write is the 1 byte for the string length followed by the string
|
||||
EXPECT_EQ(memBlock.pos(), (1 + outString.length()));
|
||||
|
||||
const char* data = dataBuffer.constData();
|
||||
EXPECT_EQ(data[0], outString.length());
|
||||
|
||||
for (int i = 0; i < outString.length(); i++)
|
||||
{
|
||||
EXPECT_EQ(data[1 + i], outString[i]);
|
||||
}
|
||||
|
||||
// now confirm that we can read it back out
|
||||
memBlock.reset();
|
||||
CArchive readArchive(&memBlock, CArchive::load);
|
||||
QString inString;
|
||||
readArchive >> inString;
|
||||
|
||||
EXPECT_EQ(inString, outString);
|
||||
}
|
||||
|
||||
TEST(CArchive, test2ByteLengthString)
|
||||
{
|
||||
int testSize = 0xff + 1; // use a test size greater than 2^8, so it takes more than 1 byte for the length
|
||||
QString outString = CreateTestString(testSize);
|
||||
|
||||
QBuffer memBlock;
|
||||
memBlock.open(QIODevice::ReadWrite);
|
||||
CArchive writeArchive(&memBlock, CArchive::store);
|
||||
writeArchive << outString;
|
||||
|
||||
QByteArray dataBuffer = memBlock.buffer();
|
||||
|
||||
// buffer size after write is the 1 marker byte (0xff), 2 bytes for the string length followed by the string
|
||||
EXPECT_EQ(memBlock.pos(), (1 + 2 + testSize));
|
||||
|
||||
const char* data = dataBuffer.constData();
|
||||
EXPECT_EQ(data[0], '\xff');
|
||||
short shortLength = 0;
|
||||
memcpy(&shortLength, data + 1, 2);
|
||||
EXPECT_EQ(shortLength, testSize);
|
||||
|
||||
for (int i = 0; i < testSize; i++)
|
||||
{
|
||||
EXPECT_EQ(data[3 + i], outString[i]);
|
||||
}
|
||||
|
||||
// now confirm that we can read it back out
|
||||
memBlock.reset();
|
||||
CArchive readArchive(&memBlock, CArchive::load);
|
||||
QString inString;
|
||||
readArchive >> inString;
|
||||
|
||||
EXPECT_EQ(inString, outString);
|
||||
}
|
||||
|
||||
TEST(CArchive, test4ByteLengthString)
|
||||
{
|
||||
int testSize = 0xffff + 1; // use a test size greater than 2^16, so it takes more than 2 bytes for the length
|
||||
QString outString = CreateTestString(testSize);
|
||||
|
||||
QBuffer memBlock;
|
||||
memBlock.open(QIODevice::ReadWrite);
|
||||
CArchive writeArchive(&memBlock, CArchive::store);
|
||||
writeArchive << outString;
|
||||
|
||||
QByteArray dataBuffer = memBlock.buffer();
|
||||
|
||||
// buffer size after write is the 1 marker byte (0xff), the 2 byte marker (0xffff), 4 bytes for the string length followed by the string
|
||||
EXPECT_EQ(memBlock.pos(), (1 + 2 + 4 + testSize));
|
||||
|
||||
const char* data = dataBuffer.constData();
|
||||
EXPECT_EQ(data[0], '\xff');
|
||||
EXPECT_EQ(data[1], '\xff');
|
||||
EXPECT_EQ(data[2], '\xff');
|
||||
uint32 doubleWordLength = 0;
|
||||
memcpy(&doubleWordLength, data + 3, 4);
|
||||
EXPECT_EQ(doubleWordLength, testSize);
|
||||
|
||||
for (int i = 0; i < testSize; i++)
|
||||
{
|
||||
EXPECT_EQ(data[7 + i], outString[i]);
|
||||
}
|
||||
|
||||
// now confirm that we can read it back out
|
||||
memBlock.reset();
|
||||
CArchive readArchive(&memBlock, CArchive::load);
|
||||
QString inString;
|
||||
readArchive >> inString;
|
||||
|
||||
EXPECT_EQ(inString, outString);
|
||||
}
|
||||
|
||||
TEST(CArchive, testWindowsWideCharacterString1ByteLength)
|
||||
{
|
||||
// NOTE: CArchive only writes out Utf-8 now, so we can't actually
|
||||
// use that class to encode to Windows wide character strings.
|
||||
// That is why this test (unlike the non-Windows wide character tests)
|
||||
// is manually creating a stream and only confirming that CArchive can decode it.
|
||||
|
||||
QString outString = CreateTestString(20); // use a test size smaller than 2^8, so that we only need one byte to store the length
|
||||
|
||||
// we have to write the 0xff marker, then the 2 byte marker to indicate it's wide characters,
|
||||
// then the actual length (1 byte), then the string, with windows wide characters (2 bytes per)
|
||||
QByteArray testBuffer;
|
||||
testBuffer.resize(1 + 2 + 1 + (outString.length() * 2));
|
||||
testBuffer[0] = '\xff';
|
||||
testBuffer[1] = '\xfe';
|
||||
testBuffer[2] = '\xff';
|
||||
testBuffer[3] = aznumeric_cast<char>(outString.length());
|
||||
|
||||
memcpy(testBuffer.data() + 4, outString.utf16(), outString.length() * 2);
|
||||
|
||||
// now confirm that we can read it back out
|
||||
QBuffer memBlock(&testBuffer);
|
||||
memBlock.open(QIODevice::ReadOnly);
|
||||
CArchive readArchive(&memBlock, CArchive::load);
|
||||
QString inString;
|
||||
readArchive >> inString;
|
||||
|
||||
EXPECT_EQ(inString, outString);
|
||||
}
|
||||
|
||||
TEST(CArchive, testWindowsWideCharacterString2ByteLength)
|
||||
{
|
||||
// NOTE: CArchive only writes out Utf-8 now, so we can't actually
|
||||
// use that class to encode to Windows wide character strings.
|
||||
// That is why this test (unlike the non-Windows wide character tests)
|
||||
// is manually creating a stream and only confirming that CArchive can decode it.
|
||||
|
||||
int testSize = 0xff + 1; // use a test size greater than 2^8, so that we need two bytes to store the length
|
||||
QString outString = CreateTestString(testSize);
|
||||
|
||||
// we have to write the 0xff marker, then the 2 byte marker to indicate it's wide characters,
|
||||
// then the marker to indicate it's more than a 1 byte length, then the actual length (2 byte),
|
||||
// then the string, with windows wide characters (2 bytes per)
|
||||
QByteArray testBuffer;
|
||||
testBuffer.resize(1 + 2 + 1 + 2 + (testSize * 2));
|
||||
testBuffer[0] = '\xff';
|
||||
testBuffer[1] = '\xfe';
|
||||
testBuffer[2] = '\xff';
|
||||
testBuffer[3] = '\xff';
|
||||
|
||||
memcpy(testBuffer.data() + 4, &testSize, 2);
|
||||
|
||||
memcpy(testBuffer.data() + 6, outString.utf16(), testSize * 2);
|
||||
|
||||
// now confirm that we can read it back out
|
||||
QBuffer memBlock(&testBuffer);
|
||||
memBlock.open(QIODevice::ReadOnly);
|
||||
CArchive readArchive(&memBlock, CArchive::load);
|
||||
QString inString;
|
||||
readArchive >> inString;
|
||||
|
||||
EXPECT_EQ(inString, outString);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
// AzTest
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
// AzCore
|
||||
#include <AzCore/Memory/AllocatorScope.h>
|
||||
|
||||
// AzFramework
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
|
||||
#include <Mocks/ICryPakMock.h>
|
||||
|
||||
using ::testing::NiceMock;
|
||||
|
||||
class EditorCoreTestEnvironment
|
||||
: public AZ::Test::ITestEnvironment
|
||||
{
|
||||
public:
|
||||
AZ_TEST_CLASS_ALLOCATOR(EditorCoreTestEnvironment);
|
||||
|
||||
virtual ~EditorCoreTestEnvironment()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetupEnvironment() override
|
||||
{
|
||||
m_allocatorScope.ActivateAllocators();
|
||||
m_cryPak = new NiceMock<CryPakMock>();
|
||||
|
||||
// Initialize the fileIO
|
||||
AZ::IO::FileIOBase::SetInstance(&m_fileIO);
|
||||
|
||||
// Setup gEnv with the systems/mocks we will be using for the unit tests
|
||||
m_stubEnv.pCryPak = m_cryPak;
|
||||
m_stubEnv.pFileIO = &m_fileIO;
|
||||
gEnv = &m_stubEnv;
|
||||
}
|
||||
|
||||
void TeardownEnvironment() override
|
||||
{
|
||||
delete m_cryPak;
|
||||
m_allocatorScope.DeactivateAllocators();
|
||||
}
|
||||
|
||||
private:
|
||||
AZ::AllocatorScope<AZ::OSAllocator, AZ::SystemAllocator, AZ::LegacyAllocator, CryStringAllocator> m_allocatorScope;
|
||||
SSystemGlobalEnvironment m_stubEnv;
|
||||
AZ::IO::LocalFileIO m_fileIO;
|
||||
NiceMock<CryPakMock>* m_cryPak;
|
||||
};
|
||||
|
||||
AZ_UNIT_TEST_HOOK(new EditorCoreTestEnvironment);
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 <AzTest/AzTest.h>
|
||||
#include "Util/PathUtil.h"
|
||||
#include <CrySystemBus.h>
|
||||
|
||||
TEST(PathUtil, GamePathToFullPath_DoesNotBufferOverflow)
|
||||
{
|
||||
// There are no test assertions in this test because the purpose is just to verify that the test runs without crashing
|
||||
QString pngExtension(".png");
|
||||
|
||||
// Create a string of lenth AZ_MAX_PATH_LEN that ends in .png
|
||||
QString longStringMaxPath(AZ_MAX_PATH_LEN, 'x');
|
||||
longStringMaxPath.replace(longStringMaxPath.length() - pngExtension.length(), longStringMaxPath.length(), pngExtension);
|
||||
Path::GamePathToFullPath(longStringMaxPath);
|
||||
|
||||
QString longStringMaxPathPlusOne(AZ_MAX_PATH_LEN + 1, 'x');
|
||||
longStringMaxPathPlusOne.replace(longStringMaxPathPlusOne.length() - pngExtension.length(), longStringMaxPathPlusOne.length(), pngExtension);
|
||||
Path::GamePathToFullPath(longStringMaxPathPlusOne);
|
||||
}
|
||||
Reference in New Issue
Block a user