Merge pull request #2094 from aws-lumberyard-dev/cgalvan/gitflow_210712

Merged stabilization/2106 to development
This commit is contained in:
Chris Galvan
2021-07-12 14:05:19 -05:00
committed by GitHub
221 changed files with 2398 additions and 9445 deletions
+1 -2
View File
@@ -47,8 +47,7 @@ set(ENABLED_GEMS
LmbrCentral
LyShine
HttpRequestor
Atom_AtomBridge
PythonCoverage
Atom
AWSCore
AWSClientAuth
AWSMetrics
@@ -51,4 +51,18 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
)
ly_add_pytest(
NAME AutomatedTesting::GameLauncherWithGPU
TEST_SUITE sandbox
TEST_REQUIRES gpu
PATH ${CMAKE_CURRENT_LIST_DIR}/test_GameLauncher_EnterExitGameMode_Works.py
TIMEOUT 100
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
AZ::PythonBindingsExample
Legacy::Editor
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
)
endif()
-1
View File
@@ -111,7 +111,6 @@ ly_add_target(
AZ::AzCore
AZ::AzToolsFramework
Gem::LmbrCentral.Static
Legacy::NewsShared
AZ::AWSNativeSDKInit
AZ::AtomCore
Gem::Atom_RPI.Edit
+16 -28
View File
@@ -458,25 +458,7 @@ void EditorViewportWidget::Update()
SetFOV(cameraState.m_fovOrZoom);
m_Camera.SetZRange(cameraState.m_nearClip, cameraState.m_farClip);
}
else if (!ed_useNewCameraSystem)
{
m_renderViewport->GetViewportContext()->SetCameraTransform(LYTransformToAZTransform(m_Camera.GetMatrix()));
}
// Don't override the game mode FOV
if (!GetIEditor()->IsInGameMode())
{
AZ::Matrix4x4 clipMatrix;
AZ::MakePerspectiveFovMatrixRH(
clipMatrix,
GetFOV(),
aznumeric_cast<float>(width()) / aznumeric_cast<float>(height()),
m_Camera.GetNearPlane(),
m_Camera.GetFarPlane(),
true
);
m_renderViewport->GetViewportContext()->SetCameraProjectionMatrix(clipMatrix);
}
// Reset the camera update flag now that we're finished updating our viewport context
m_updateCameraPositionNextTick = false;
@@ -632,7 +614,6 @@ void EditorViewportWidget::SetViewEntity(const AZ::EntityId& viewEntityId, bool
void EditorViewportWidget::ResetToViewSourceType(const ViewSourceType& viewSourceType)
{
LockCameraMovement(true);
m_pCameraFOVVariable = nullptr;
m_viewEntityId.SetInvalid();
m_cameraObjectId = GUID_NULL;
m_viewSourceType = viewSourceType;
@@ -2547,13 +2528,26 @@ void EditorViewportWidget::CenterOnSliceInstance()
//////////////////////////////////////////////////////////////////////////
void EditorViewportWidget::SetFOV(float fov)
{
if (m_pCameraFOVVariable)
if (m_viewEntityId.IsValid())
{
m_pCameraFOVVariable->Set(fov);
Camera::CameraRequestBus::Event(m_viewEntityId, &Camera::CameraComponentRequests::SetFov, AZ::RadToDeg(fov));
}
else
{
m_camFOV = fov;
// Set the active camera's FOV
{
AZ::Matrix4x4 clipMatrix;
AZ::MakePerspectiveFovMatrixRH(
clipMatrix,
GetFOV(),
aznumeric_cast<float>(width()) / aznumeric_cast<float>(height()),
m_Camera.GetNearPlane(),
m_Camera.GetFarPlane(),
true
);
m_renderViewport->GetViewportContext()->SetCameraProjectionMatrix(clipMatrix);
}
}
if (m_viewPane)
@@ -2580,13 +2574,7 @@ float EditorViewportWidget::GetFOV() const
}
}
if (m_pCameraFOVVariable)
{
float fov;
m_pCameraFOVVariable->Get(fov);
return fov;
}
else if (m_viewEntityId.IsValid())
if (m_viewEntityId.IsValid())
{
float fov = AZ::RadToDeg(m_camFOV);
Camera::CameraRequestBus::EventResult(fov, m_viewEntityId, &Camera::CameraComponentRequests::GetFov);
-1
View File
@@ -506,7 +506,6 @@ protected:
CPredefinedAspectRatios m_predefinedAspectRatios;
IVariable* m_pCameraFOVVariable = nullptr;
bool m_bCursorHidden = false;
void OnMenuResolutionCustom();
+13
View File
@@ -41,6 +41,7 @@ AZ_POP_DISABLE_WARNING
// AzToolsFramework
#include <AzToolsFramework/Application/Ticker.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/EditorAnimationSystemRequestBus.h>
#include <AzToolsFramework/SourceControl/QtSourceControlNotificationHandler.h>
#include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
@@ -93,6 +94,8 @@ AZ_POP_DISABLE_WARNING
#include "AssetEditor/AssetEditorWindow.h"
#include "ActionManager.h"
#include <ImGuiBus.h>
using namespace AZ;
using namespace AzQtComponents;
using namespace AzToolsFramework;
@@ -489,6 +492,16 @@ void MainWindow::Initialize()
ActionOverrideRequestBus::Event(
GetEntityContextId(), &ActionOverrideRequests::SetupActionOverrideHandler, this);
if (auto imGuiManager = AZ::Interface<ImGui::IImGuiManager>::Get())
{
auto handleImGuiStateChangeFn = [](bool enabled)
{
EditorWindowUIRequestBus::Broadcast(&EditorWindowUIRequests::SetEditorUiEnabled, enabled);
};
m_handleImGuiStateChangeHandler = ImGui::IImGuiManager::ImGuiSetEnabledEvent::Handler(handleImGuiStateChangeFn);
imGuiManager->ConnectImGuiSetEnabledChangedHandler(m_handleImGuiStateChangeHandler);
}
AzToolsFramework::EditorEventsBus::Broadcast(&AzToolsFramework::EditorEvents::NotifyMainWindowInitialized, this);
}
+1
View File
@@ -249,6 +249,7 @@ private:
QPointer<ToolbarCustomizationDialog> m_toolbarCustomizationDialog;
QScopedPointer<AzToolsFramework::QtSourceControlNotificationHandler> m_sourceControlNotifHandler;
AZ::Event<bool>::Handler m_handleImGuiStateChangeHandler;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
static MainWindow* m_instance;
@@ -27,6 +27,7 @@ ly_add_target(
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
3rdParty::Qt::Widgets
AZ::AzCore
AZ::AzToolsFramework
Legacy::CryCommon
@@ -66,3 +66,5 @@ namespace EntityOutliner
emit OnOptionToggled(DisplayOption::AutoExpand, checked);
}
}
#include <UI/Outliner/moc_OutlinerDisplayOptionsMenu.cpp>
@@ -32,7 +32,24 @@ bool OutlinerSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelI
bool OutlinerSortFilterProxyModel::lessThan(const QModelIndex& leftIndex, const QModelIndex& rightIndex) const
{
return sourceModel()->data(leftIndex).toString() < sourceModel()->data(rightIndex).toString();
if (leftIndex.isValid() && rightIndex.isValid())
{
QVariant leftData = sourceModel()->data(leftIndex);
QVariant rightData = sourceModel()->data(rightIndex);
// make sure to compare the correct data types for sorting the current column
AZ_Assert(leftData.type() == rightData.type(), "OutlinerSortFilterProxyModel::lessThan types do not agree!");
if (static_cast<QMetaType::Type>(leftData.type()) == QMetaType::QString)
{
return leftData.toString() < rightData.toString();
}
else if (static_cast<QMetaType::Type>(leftData.type()) == QMetaType::ULongLong)
{
return leftData.toULongLong() < rightData.toULongLong();
}
AZ_Error("Editor", false, "Error! Unhandled type \"%s\" in OutlinerSortFilterProxyModel::lessThan", leftData.typeName());
}
return false;
}
void OutlinerSortFilterProxyModel::sort(int /*column*/, Qt::SortOrder /*order*/)
@@ -269,10 +269,12 @@ OutlinerWidget::OutlinerWidget(QWidget* pParent, Qt::WindowFlags flags)
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
AzToolsFramework::GetEntityContextId());
AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusConnect();
AzToolsFramework::EditorWindowUIRequestBus::Handler::BusConnect();
}
OutlinerWidget::~OutlinerWidget()
{
AzToolsFramework::EditorWindowUIRequestBus::Handler::BusDisconnect();
AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
@@ -1321,16 +1323,25 @@ static void SetEntityOutlinerState(Ui::OutlinerWidgetUI* entityOutlinerUi, const
AzQtComponents::SetWidgetInteractEnabled(entityOutlinerUi->m_searchWidget, on);
}
void OutlinerWidget::EnableUi(bool enable)
{
SetEntityOutlinerState(m_gui, enable);
setEnabled(enable);
}
void OutlinerWidget::SetEditorUiEnabled(bool enable)
{
EnableUi(enable);
}
void OutlinerWidget::EnteredComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
{
SetEntityOutlinerState(m_gui, false);
setEnabled(false);
EnableUi(false);
}
void OutlinerWidget::LeftComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
{
setEnabled(true);
SetEntityOutlinerState(m_gui, true);
EnableUi(true);
}
void OutlinerWidget::OnSliceInstantiated(const AZ::Data::AssetId& /*sliceAssetId*/, AZ::SliceComponent::SliceInstanceAddress& sliceAddress, const AzFramework::SliceInstantiationTicket& /*ticket*/)
@@ -13,6 +13,7 @@
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/base.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
@@ -57,6 +58,7 @@ class OutlinerWidget
, private AzToolsFramework::SliceEditorEntityOwnershipServiceNotificationBus::Handler
, private AzToolsFramework::EditorEntityInfoNotificationBus::Handler
, private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, private AzToolsFramework::EditorWindowUIRequestBus::Handler
{
Q_OBJECT;
public:
@@ -106,6 +108,9 @@ private:
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
// EditorWindowUIRequestBus overrides
void SetEditorUiEnabled(bool enable) override;
// Build a selection object from the given entities. Entities already in the Widget's selection buffers are ignored.
template <class EntityIdCollection>
QItemSelection BuildSelectionFromEntities(const EntityIdCollection& entityIds);
@@ -171,6 +176,7 @@ private:
AZ::EntityId GetEntityIdFromIndex(const QModelIndex& index) const;
QModelIndex GetIndexFromEntityId(const AZ::EntityId& entityId) const;
void ExtractEntityIdsFromSelection(const QItemSelection& selection, AzToolsFramework::EntityIdList& entityIdList) const;
void EnableUi(bool enable);
// AzToolsFramework::OutlinerModelNotificationBus::Handler
// Receive notification from the outliner model that we should scroll
@@ -23,6 +23,7 @@ ly_add_target(
.
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
AZ::AzCore
Legacy::CryCommon
Legacy::EditorLib
@@ -17,19 +17,8 @@
/////////////////////////////////////////////////////////////////////////////
#include <platform.h>
/////////////////////////////////////////////////////////////////////////////
// STL
/////////////////////////////////////////////////////////////////////////////
#include <vector>
#include <list>
#include <map>
#include <set>
#include <algorithm>
/////////////////////////////////////////////////////////////////////////////
// CRY Stuff ////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
#include <ISystem.h>
#include "Util/EditorUtils.h"
#include "EditorCoreAPI.h"
@@ -31,6 +31,8 @@ ly_add_target(
.
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
3rdParty::Qt::Widgets
AZ::AzCore
Legacy::CryCommon
Legacy::EditorLib
+14
View File
@@ -525,6 +525,7 @@ QtViewPaneManager::QtViewPaneManager(QObject* parent)
// view pane manager is interested when we enter/exit ComponentMode
m_componentModeNotifications.BusConnect(AzToolsFramework::GetEntityContextId());
m_windowRequest.BusConnect();
m_componentModeNotifications.SetEnteredComponentModeFunc(
[this](const AZStd::vector<AZ::Uuid>& /*componentModeTypes*/)
@@ -545,10 +546,23 @@ QtViewPaneManager::QtViewPaneManager(QObject* parent)
AzQtComponents::SetWidgetInteractEnabled(widget, on);
});
});
m_windowRequest.SetEnableEditorUiFunc(
[this](bool enable)
{
// gray out panels when entering ImGui mode
SetDefaultActionsEnabled(
enable, m_registeredPanes,
[](QWidget* widget, bool on)
{
AzQtComponents::SetWidgetInteractEnabled(widget, on);
});
});
}
QtViewPaneManager::~QtViewPaneManager()
{
m_windowRequest.BusDisconnect();
m_componentModeNotifications.BusDisconnect();
}
+7 -2
View File
@@ -21,6 +21,7 @@
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <QObject>
#include <QVector>
@@ -249,8 +250,12 @@ private:
QPointer<AzQtComponents::FancyDocking> m_advancedDockManager;
using EditorComponentModeNotificationBusImpl = AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBusImpl;
EditorComponentModeNotificationBusImpl m_componentModeNotifications; /**< Helper for EditorComponentModeNotificationBus so
* QtViewPaneManager does not need to inherit directly from it. */
EditorComponentModeNotificationBusImpl m_componentModeNotifications; //!< Helper for EditorComponentModeNotificationBus so
//!< QtViewPaneManager does not need to inherit directly from it. */
using EditorWindowRequestBusImpl = AzToolsFramework::EditorWindowRequestBusImpl;
EditorWindowRequestBusImpl m_windowRequest; //!< Helper for EditorWindowRequestBus so
//!< QtViewPaneManager does not need to inherit directly from it. */
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
@@ -40,10 +40,6 @@
#include "CryEdit.h"
#include "LevelFileDialog.h"
// NewsShared
#include <NewsShared/ResourceManagement/ResourceManifest.h> // for News::ResourceManifest
#include <NewsShared/Qt/ArticleViewContainer.h> // for News::ArticleViewContainer
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <WelcomeScreen/ui_WelcomeScreenDialog.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
@@ -8,8 +8,6 @@
#if !defined(Q_MOC_RUN)
#include <QDialog>
#include "NewsShared/LogType.h"
#include "NewsShared/ErrorCodes.h"
#endif
namespace News {
@@ -62,9 +60,6 @@ private:
void OnRecentLevelTableItemClicked(const QModelIndex& index);
void OnCloseBtnClicked(bool checked);
void SyncFail(News::ErrorCode error);
void SyncSuccess();
private Q_SLOTS:
void previewAreaScrolled();
};
+18 -5
View File
@@ -27,11 +27,17 @@ namespace AZ
template <typename... Params>
EventHandler<Params...>::EventHandler(const EventHandler& rhs)
: m_callback(rhs.m_callback)
, m_event(rhs.m_event)
{
// Copy the callback function, then perform a Connect with the new event
if (rhs.m_event)
// Copy the callback and event, then perform a Connect to the event
if (m_callback && m_event)
{
rhs.m_event->Connect(*this);
m_event->Connect(*this);
}
else
{
// It was not possible to connect to the event, set it to nullptr
m_event = nullptr;
}
}
@@ -65,9 +71,16 @@ namespace AZ
{
Disconnect();
m_callback = rhs.m_callback;
if (rhs.m_event)
m_event = rhs.m_event;
// Copy the callback and event, then perform a Connect to the event
if (m_callback && m_event)
{
rhs.m_event->Connect(*this);
m_event->Connect(*this);
}
else
{
// It was not possible to connect to the event, set it to nullptr
m_event = nullptr;
}
}
@@ -32,6 +32,15 @@ namespace AZ
{
if (m_hashes.size() < m_hashes.max_size())
{
constexpr size_t MaxTagSize = TagName{}.max_size();
if (specialization.size() > MaxTagSize)
{
AZ_Error("Settings Registry", false, R"(Cannot append Specialization tag of "%.*s.")"
" It is longer than the MaxTagNameSize of %zu", AZ_STRING_ARG(specialization),
MaxTagSize);
return false;
}
m_names.push_back(TagName{ specialization });
TagName& tag = m_names.back();
AZStd::to_lower(tag.begin(), tag.end());
@@ -49,7 +49,7 @@ namespace AZ
class Specializations
{
public:
static constexpr size_t MaxTagNameSize = 32;
static constexpr size_t MaxTagNameSize = 64;
static constexpr size_t MaxCount = 15;
static constexpr size_t NotFound = static_cast<size_t>(-1);
using TagName = AZStd::fixed_string<MaxTagNameSize>;
@@ -146,7 +146,7 @@ namespace AZ {
AZStd::mutex g_dbgLoadingMutex;
HANDLE g_currentProcess = 0; /// We deal with only one process for now.
CRITICAL_SECTION g_csDbgHelpDll; /// All dbg help functions are single threaded, so we need to control the access.
AZStd::fixed_vector<SymbolStorage::ModuleInfo, 256> g_moduleInfo;
AZStd::fixed_vector<SymbolStorage::ModuleInfo, 2048> g_moduleInfo;
// reserve 4k of scratch space so that we can get some callstack information without any allocations and as little stack frame usage as possible.
const size_t g_scratchSpaceSize = 2048;
@@ -257,6 +257,48 @@ namespace UnitTest
EXPECT_FALSE(testEvent1.HasHandlerConnected());
EXPECT_TRUE(testEvent2.HasHandlerConnected());
}
TEST_F(EventTests, TestHandlerCopyConstructorOperator)
{
int32_t invokedCounter = 0;
AZ::Event<> testEvent;
AZ::Event<>::Handler testHandler([&invokedCounter]() { invokedCounter++; });
testHandler.Connect(testEvent);
AZ::Event<>::Handler testHandler2(testHandler);
EXPECT_TRUE(testHandler.IsConnected());
EXPECT_TRUE(testHandler2.IsConnected());
EXPECT_TRUE(invokedCounter == 0);
testEvent.Signal();
EXPECT_TRUE(invokedCounter == 2);
}
TEST_F(EventTests, TestHandlerCopyAssignmentOperator)
{
int32_t invokedCounter = 0;
AZ::Event<> testEvent;
AZ::Event<>::Handler testHandler([&invokedCounter]() { invokedCounter++; });
testHandler.Connect(testEvent);
AZ::Event<>::Handler testHandler2;
EXPECT_TRUE(testHandler.IsConnected());
EXPECT_FALSE(testHandler2.IsConnected());
testHandler2 = testHandler;
EXPECT_TRUE(testHandler2.IsConnected());
EXPECT_TRUE(invokedCounter == 0);
testEvent.Signal();
EXPECT_TRUE(invokedCounter == 2);
}
}
#if defined(HAVE_BENCHMARK)
@@ -12,5 +12,5 @@
#define AZ_TRAIT_AZFRAMEWORK_USE_ERRNO_T_TYPEDEF 1
#define AZ_TRAIT_AZFRAMEWORK_USE_POSIX_LOCALTIME_R 0
#define AZ_TRAIT_AZFRAMEWORK_PYTHON_SHELL "python.sh"
#define AZ_TRAIT_AZFRAMEWORK_USE_PROJECT_MANAGER 0
#define AZ_TRAIT_AZFRAMEWORK_USE_PROJECT_MANAGER 1
#define AZ_TRAIT_AZFRAMEWORK_PROCESSLAUNCH_DEFAULT 0
@@ -12,5 +12,5 @@
#define AZ_TRAIT_AZFRAMEWORK_USE_ERRNO_T_TYPEDEF 0
#define AZ_TRAIT_AZFRAMEWORK_USE_POSIX_LOCALTIME_R 1
#define AZ_TRAIT_AZFRAMEWORK_PYTHON_SHELL "python.sh"
#define AZ_TRAIT_AZFRAMEWORK_USE_PROJECT_MANAGER 0
#define AZ_TRAIT_AZFRAMEWORK_USE_PROJECT_MANAGER 1
#define AZ_TRAIT_AZFRAMEWORK_PROCESSLAUNCH_DEFAULT 0
@@ -36,8 +36,47 @@ namespace AzToolsFramework
/// Retrieve the main application window.
virtual QWidget* GetAppMainWindow() { return nullptr; }
};
using EditorWindowRequestBus = AZ::EBus<EditorWindowRequests>;
class EditorWindowUIRequests : public AZ::EBusTraits
{
public:
using Bus = AZ::EBus<EditorWindowUIRequests>;
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
//////////////////////////////////////////////////////////////////////////
/// Enable/Disable the Editor UI.
virtual void SetEditorUiEnabled([[maybe_unused]] bool enable) {}
};
using EditorWindowUIRequestBus = AZ::EBus<EditorWindowUIRequests>;
using EnableUiFunction = AZStd::function<void(bool)>;
/// Helper for EditorWindowRequests to be used as a
/// member instead of inheriting from EBus directly.
class EditorWindowRequestBusImpl
: public EditorWindowUIRequestBus::Handler
{
public:
/// Set the function to be called when entering ImGui Mode.
void SetEnableEditorUiFunc(const EnableUiFunction enableEditorUiFunc)
{
m_enableEditorUiFunc = AZStd::move(enableEditorUiFunc);
}
private:
// EditorWindowRequestBus
void SetEditorUiEnabled( [[maybe_unused]] bool enable) override
{
m_enableEditorUiFunc(enable);
}
EnableUiFunction m_enableEditorUiFunc; ///< Function to call when entering ImGui Mode.
};
} // namespace AzToolsFramework
#endif // AZTOOLSFRAMEWORK_EDITORWINDOWREQUESTBUS_H
@@ -34,7 +34,24 @@ namespace AzToolsFramework
bool EntityOutlinerSortFilterProxyModel::lessThan(const QModelIndex& leftIndex, const QModelIndex& rightIndex) const
{
return sourceModel()->data(leftIndex).toString() < sourceModel()->data(rightIndex).toString();
if (leftIndex.isValid() && rightIndex.isValid())
{
QVariant leftData = sourceModel()->data(leftIndex);
QVariant rightData = sourceModel()->data(rightIndex);
// make sure to compare the correct data types for sorting the current column
AZ_Assert(leftData.type() == rightData.type(), "EntityOutlinerSortFilterProxyModel::lessThan types do not agree!");
if (static_cast<QMetaType::Type>(leftData.type()) == QMetaType::QString)
{
return leftData.toString() < rightData.toString();
}
else if (static_cast<QMetaType::Type>(leftData.type()) == QMetaType::ULongLong)
{
return leftData.toULongLong() < rightData.toULongLong();
}
AZ_Error("Editor", false, "Error! Unhandled type \"%s\" in EntityOutlinerSortFilterProxyModel::lessThan", leftData.typeName());
}
return false;
}
void EntityOutlinerSortFilterProxyModel::sort(int /*column*/, Qt::SortOrder /*order*/)
@@ -291,10 +291,12 @@ namespace AzToolsFramework
GetEntityContextId());
EditorEntityInfoNotificationBus::Handler::BusConnect();
Prefab::PrefabPublicNotificationBus::Handler::BusConnect();
EditorWindowUIRequestBus::Handler::BusConnect();
}
EntityOutlinerWidget::~EntityOutlinerWidget()
{
EditorWindowUIRequestBus::Handler::BusDisconnect();
Prefab::PrefabPublicNotificationBus::Handler::BusDisconnect();
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
EditorEntityInfoNotificationBus::Handler::BusDisconnect();
@@ -1106,16 +1108,25 @@ namespace AzToolsFramework
AzQtComponents::SetWidgetInteractEnabled(entityOutlinerUi->m_searchWidget, on);
}
void EntityOutlinerWidget::EnableUi(bool enable)
{
SetEntityOutlinerState(m_gui, enable);
setEnabled(enable);
}
void EntityOutlinerWidget::SetEditorUiEnabled(bool enable)
{
EnableUi(enable);
}
void EntityOutlinerWidget::EnteredComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
{
SetEntityOutlinerState(m_gui, false);
setEnabled(false);
EnableUi(false);
}
void EntityOutlinerWidget::LeftComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
{
setEnabled(true);
SetEntityOutlinerState(m_gui, true);
EnableUi(true);
}
void EntityOutlinerWidget::OnPrefabInstancePropagationBegin()
@@ -11,6 +11,7 @@
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/base.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
@@ -58,6 +59,7 @@ namespace AzToolsFramework
, private EditorEntityInfoNotificationBus::Handler
, private ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, private Prefab::PrefabPublicNotificationBus::Handler
, private EditorWindowUIRequestBus::Handler
{
Q_OBJECT;
public:
@@ -105,6 +107,9 @@ namespace AzToolsFramework
void OnPrefabInstancePropagationBegin() override;
void OnPrefabInstancePropagationEnd() override;
// EditorWindowUIRequestBus overrides
void SetEditorUiEnabled(bool enable) override;
// Build a selection object from the given entities. Entities already in the Widget's selection buffers are ignored.
template <class EntityIdCollection>
QItemSelection BuildSelectionFromEntities(const EntityIdCollection& entityIds);
@@ -155,6 +160,7 @@ namespace AzToolsFramework
AZ::EntityId GetEntityIdFromIndex(const QModelIndex& index) const;
QModelIndex GetIndexFromEntityId(const AZ::EntityId& entityId) const;
void ExtractEntityIdsFromSelection(const QItemSelection& selection, EntityIdList& entityIdList) const;
void EnableUi(bool enable);
// OutlinerModelNotificationBus::Handler
// Receive notification from the outliner model that we should scroll
@@ -376,6 +376,7 @@ namespace AzToolsFramework
ToolsApplicationEvents::Bus::Handler::BusConnect();
AZ::EntitySystemBus::Handler::BusConnect();
EntityPropertyEditorRequestBus::Handler::BusConnect();
EditorWindowUIRequestBus::Handler::BusConnect();
m_spacer = nullptr;
m_emptyIcon = QIcon();
@@ -421,6 +422,7 @@ namespace AzToolsFramework
{
qApp->removeEventFilter(this);
EditorWindowUIRequestBus::Handler::BusDisconnect();
EntityPropertyEditorRequestBus::Handler::BusDisconnect();
ToolsApplicationEvents::Bus::Handler::BusDisconnect();
AZ::EntitySystemBus::Handler::BusDisconnect();
@@ -4961,6 +4963,27 @@ namespace AzToolsFramework
EnableDisableComponentActions(widget, actions, false);
}
void EntityPropertyEditor::SetEditorUiEnabled(bool enable)
{
if (enable)
{
EnableComponentActions(this, m_entityComponentActions);
}
else
{
DisableComponentActions(this, m_entityComponentActions);
}
m_disabled = !enable;
SetPropertyEditorState(m_gui, enable);
for (auto componentEditor : m_componentEditors)
{
AzQtComponents::SetWidgetInteractEnabled(componentEditor, enable);
}
// record the selected state after entering/leaving component mode
SaveComponentEditorState();
}
void EntityPropertyEditor::EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes)
{
DisableComponentActions(this, m_entityComponentActions);
@@ -21,6 +21,7 @@
#include <AzCore/Asset/AssetCommon.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/Undo/UndoSystem.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/API/EntityPropertyEditorRequestsBus.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
@@ -108,6 +109,7 @@ namespace AzToolsFramework
, public EditorInspectorComponentNotificationBus::MultiHandler
, private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, public AZ::EntitySystemBus::Handler
, private EditorWindowUIRequestBus::Handler
{
Q_OBJECT;
public:
@@ -208,6 +210,9 @@ namespace AzToolsFramework
void GetSelectedEntities(EntityIdList& selectedEntityIds) override;
void SetNewComponentId(AZ::ComponentId componentId) override;
// EditorWindowRequestBus overrides
void SetEditorUiEnabled(bool enable) override;
bool IsEntitySelected(const AZ::EntityId& id) const;
bool IsSingleEntitySelected(const AZ::EntityId& id) const;
@@ -1,6 +1,6 @@
/*
* 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
*
*/
@@ -277,7 +277,7 @@ void FileWatcherUnitTestRunner::StartTest()
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(newName3).toLower());
#if !defined(AZ_PLATFORM_LINUX)
// final test... make sure that renaming a DIRECTORY works too.
// final test... make sure that renaming a DIRECTORY works too.
// Note that linux does not get any callbacks if just the directory is renamed (from inotify)
QDir renamer;
fileAddCalled = false;
@@ -312,6 +312,9 @@ void FileWatcherUnitTestRunner::StartTest()
Q_EMIT UnitTestPassed();
}
#if !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
// File operations on Linux behave differently on Linux than Windows.
// The system under test doesn't yet handle Linux's file operations, which surfaced as this test arbitrarily passing and failing.
// This test is disabled on Linux until the system under test can handle Linux file system behavior correctly.
#if !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS && !defined(AZ_PLATFORM_LINUX)
REGISTER_UNIT_TEST(FileWatcherUnitTestRunner)
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
-1
View File
@@ -10,7 +10,6 @@ add_subdirectory(AssetProcessor)
add_subdirectory(AWSNativeSDKInit)
add_subdirectory(AzTestRunner)
add_subdirectory(CrashHandler)
add_subdirectory(News)
add_subdirectory(PythonBindingsExample)
add_subdirectory(RemoteConsole)
add_subdirectory(DeltaCataloger)
-31
View File
@@ -1,31 +0,0 @@
#
# 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
#
#
add_subdirectory(NewsBuilder)
if (NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_add_target(
NAME NewsShared STATIC
NAMESPACE Legacy
AUTOMOC
AUTOUIC
AUTORCC
FILES_CMAKE
news_shared_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
3rdParty::Qt::Network
3rdParty::Qt::Widgets
AZ::AzQtComponents
)
@@ -1,38 +0,0 @@
#
# 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
#
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
if (NOT PAL_TRAIT_BUILD_NEWSBUILDER_SUPPORTED OR NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_add_target(
NAME NewsBuilder EXECUTABLE
NAMESPACE Legacy
AUTOMOC
AUTOUIC
AUTORCC
FILES_CMAKE
news_builder_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
.
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
3rdParty::Qt::Widgets
3rdParty::AWSNativeSDK::Dependencies
3rdParty::AWSNativeSDK::Core
3rdParty::AWSNativeSDK::S3
Legacy::NewsShared
AZ::AzCore
AZ::AzQtComponents
AZ::AzFramework
)
@@ -1,212 +0,0 @@
/*
* 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 "EndpointManager.h"
#include <NewsShared/ResourceManagement/ResourceManifest.h>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QCoreApplication>
namespace News
{
Endpoint::Endpoint(QString name, QString awsProfile, QString url, QString bucket)
: m_name(name)
, m_awsProfile(awsProfile)
, m_url(url)
, m_bucket(bucket)
{}
Endpoint::Endpoint(const QJsonObject& json)
: Endpoint(json["name"].toString(),
json["awsProfile"].toString(),
json["url"].toString(),
json["bucket"].toString())
{
}
Endpoint::~Endpoint() {}
void Endpoint::Write(QJsonObject& json) const
{
json["name"] = m_name;
json["awsProfile"] = m_awsProfile;
json["url"] = m_url;
json["bucket"] = m_bucket;
}
QString Endpoint::GetName() const
{
return m_name;
}
void Endpoint::SetName(const QString& name)
{
m_name = QString(name);
}
QString Endpoint::GetAwsProfile() const
{
return m_awsProfile;
}
void Endpoint::SetAwsProfile(const QString& awsProfile)
{
m_awsProfile = QString(awsProfile);
}
QString Endpoint::GetUrl() const
{
return m_url;
}
void Endpoint::SetUrl(const QString& url)
{
m_url = QString(url);
}
QString Endpoint::GetBucket() const
{
return m_bucket;
}
void Endpoint::SetBucket(const QString& bucket)
{
m_bucket = bucket;
}
EndpointManager::EndpointManager()
{
Load();
}
EndpointManager::~EndpointManager()
{
ClearEndpoints();
}
void EndpointManager::Load()
{
ClearEndpoints();
QFile file("newsBuilderConfig.txt");
if (file.open(QIODevice::ReadOnly))
{
QTextStream in(&file);
QString str = in.readAll().trimmed();
QByteArray array(str.toStdString().c_str());
QJsonDocument doc(QJsonDocument::fromJson(array));
QJsonObject json = doc.object();
QJsonArray endpointArray = json["endpoints"].toArray();
for (auto endpointDoc : endpointArray)
{
m_endpoints.append(new Endpoint(endpointDoc.toObject()));
}
int endpointIndex = json["currentEndpointIndex"].toInt();
if (endpointIndex >= 0 && m_endpoints.count() > endpointIndex)
{
m_selectedEndpoint = m_endpoints[endpointIndex];
}
else
{
m_selectedEndpoint = nullptr;
}
file.close();
SaveUrl();
}
}
void EndpointManager::Save()
{
QFile file("newsBuilderConfig.txt");
if (file.open(QIODevice::WriteOnly))
{
QJsonObject json;
QJsonArray endpointArray;
for (auto endpoint : m_endpoints)
{
QJsonObject endpointObject;
endpoint->Write(endpointObject);
endpointArray.append(endpointObject);
}
json["endpoints"] = endpointArray;
json["currentEndpointIndex"] = m_endpoints.indexOf(m_selectedEndpoint);
QTextStream out(&file);
QJsonDocument doc(json);
out << doc.toJson(QJsonDocument::Compact);
file.close();
SaveUrl();
}
}
void EndpointManager::SelectEndpoint(Endpoint* endpoint)
{
m_selectedEndpoint = endpoint;
}
void EndpointManager::AddEndpoint(Endpoint* endpoint)
{
m_endpoints.append(endpoint);
}
void EndpointManager::RemoveEndpoint(Endpoint* endpoint)
{
if (endpoint == m_selectedEndpoint)
{
m_selectedEndpoint = nullptr;
}
m_endpoints.removeAll(endpoint);
delete endpoint;
}
Endpoint* EndpointManager::GetSelectedEndpoint() const
{
return m_selectedEndpoint;
}
QList<Endpoint*>::const_iterator EndpointManager::begin() const
{
return m_endpoints.begin();
}
QList<Endpoint*>::const_iterator EndpointManager::end() const
{
return m_endpoints.end();
}
void EndpointManager::ClearEndpoints()
{
for (auto endpoint : m_endpoints)
{
delete endpoint;
}
m_endpoints.clear();
}
void EndpointManager::SaveUrl() const
{
if (!m_selectedEndpoint)
{
return;
}
// Editor looks for config file in /dev folder, so this file is placed in parent directory
QFile file(QCoreApplication::applicationDirPath() + "/newsConfig.txt");
if (file.open(QIODevice::WriteOnly))
{
QTextStream out(&file);
out << m_selectedEndpoint->GetUrl();
file.close();
}
}
}
@@ -1,77 +0,0 @@
/*
* 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 <QList>
#include <qobjectdefs.h>
class QJsonObject;
namespace News
{
//! Endpoint represents a location of news data
class Endpoint
{
public:
Endpoint(QString name, QString awsProfile, QString url, QString bucket);
explicit Endpoint(const QJsonObject& json);
~Endpoint();
void Write(QJsonObject& json) const;
QString GetName() const;
void SetName(const QString& name);
QString GetAwsProfile() const;
void SetAwsProfile(const QString& awsProfile);
QString GetUrl() const;
void SetUrl(const QString& url);
QString GetBucket() const;
void SetBucket(const QString& bucket);
private:
//! name of endpoint to distinguish with others
QString m_name;
//! name of aws credentials file
QString m_awsProfile;
//! url location of news data (e.g. cloudfront url)
QString m_url;
//! name of s3 bucket where news data resides
QString m_bucket;
};
//! EndpointManager handles endpoint collection
class EndpointManager
{
public:
EndpointManager();
~EndpointManager();
//! Load endpoints file
void Load();
//! Save endpoints file
void Save();
void SelectEndpoint(Endpoint* endpoint);
void AddEndpoint(Endpoint* endpoint);
void RemoveEndpoint(Endpoint* endpoint);
Endpoint* GetSelectedEndpoint() const;
QList<Endpoint*>::const_iterator begin() const;
QList<Endpoint*>::const_iterator end() const;
private:
QList<Endpoint*> m_endpoints;
Endpoint* m_selectedEndpoint = nullptr;
void ClearEndpoints();
//! Saves news config file which is used by LY Editor to determine news location
void SaveUrl() const;
};
}
@@ -1,8 +0,0 @@
#
# 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
#
#
set(PAL_TRAIT_BUILD_NEWSBUILDER_SUPPORTED FALSE)
@@ -1,8 +0,0 @@
#
# 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
#
#
set(PAL_TRAIT_BUILD_NEWSBUILDER_SUPPORTED FALSE)
@@ -1,8 +0,0 @@
#
# 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
#
#
set(PAL_TRAIT_BUILD_NEWSBUILDER_SUPPORTED FALSE)
@@ -1,8 +0,0 @@
#
# 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
#
#
set(PAL_TRAIT_BUILD_NEWSBUILDER_SUPPORTED TRUE)
@@ -1,8 +0,0 @@
#
# 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
#
#
set(PAL_TRAIT_BUILD_NEWSBUILDER_SUPPORTED FALSE)
@@ -1,295 +0,0 @@
/*
* 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 "ArticleDetails.h"
#include "SelectImage.h"
#include <NewsShared/ResourceManagement/Resource.h>
#include "ResourceManagement/BuilderResourceManifest.h"
#include "ResourceManagement/ImageDescriptor.h"
#include "NewsShared/ErrorCodes.h"
#include <AzCore/Casting/numeric_cast.h>
#include <QButtonGroup>
#include <QFileDialog>
#include <QMessageBox>
#include <QTimer>
#include "Qt/ui_ArticleDetails.h"
namespace News
{
ArticleDetails::ArticleDetails(QWidget* parent,
Resource& resource,
BuilderResourceManifest& manifest)
: QWidget(parent)
, m_ui(new Ui::ArticleDetailsWidget)
, m_filename("")
, m_article(resource)
, m_manifest(manifest)
, m_imageIdOriginal("")
, m_imageId("")
{
m_ui->setupUi(this);
m_ui->uidLabel->setText(QString("Article: %1").arg(m_article.GetResource().GetId()));
resizePreviewImage();
//! try to load image icon
auto pImageResource = m_manifest.FindById(m_article.GetImageId());
if (pImageResource)
{
m_imageIdOriginal = pImageResource->GetId();
SetImage(*pImageResource);
}
else
{
SetNoImage();
}
m_ui->titleText->setText(m_article.GetTitle());
m_ui->descriptionText->setPlainText(m_article.GetBody());
connect(m_ui->fromFileButton, &QPushButton::clicked, this, &ArticleDetails::OpenImageFromFile);
connect(m_ui->fromResourceButton, &QPushButton::clicked, this, &ArticleDetails::OpenImageFromResource);
connect(m_ui->clearImageButton, &QPushButton::clicked, this, &ArticleDetails::ClearImage);
connect(m_ui->updateButton, &QPushButton::clicked, this, &ArticleDetails::UpdateArticle);
connect(m_ui->deleteButton, &QPushButton::clicked, this, &ArticleDetails::DeleteArticle);
connect(m_ui->cancelButton, &QPushButton::clicked, this, &ArticleDetails::Close);
connect(m_ui->upButton, &QPushButton::clicked, this, &ArticleDetails::MoveUp);
connect(m_ui->downButton, &QPushButton::clicked, this, &ArticleDetails::MoveDown);
}
ArticleDetails::~ArticleDetails() {}
void ArticleDetails::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
resizePreviewImage();
}
void ArticleDetails::resizePreviewImage()
{
// Resize the imagePreview to keep the proportions
int newHeight = aznumeric_cast<int>(m_imageRatio * width());
m_ui->imagePreview->setFixedHeight(newHeight);
}
ArticleDescriptor& ArticleDetails::GetArticle()
{
return m_article;
}
QString ArticleDetails::GetId() const
{
return m_article.GetResource().GetId();
}
void ArticleDetails::OpenImageFromFile()
{
m_filename = QFileDialog::getOpenFileName(this,
"Open Image", ".", "Image Files (*.png *.jpg *.bmp)");
SetImage(m_filename);
}
void ArticleDetails::OpenImageFromResource()
{
m_pSelectImage = new SelectImage(m_manifest);
if (m_pSelectImage->exec() == QDialog::DialogCode::Accepted)
{
auto pSelectedResource = m_pSelectImage->GetSelected();
if (!pSelectedResource)
{
QMessageBox msgBox(QMessageBox::Critical,
"Error",
"No resource pSelectedResource",
QMessageBox::Ok,
this);
msgBox.exec();
return;
}
SetImage(*pSelectedResource);
}
delete m_pSelectImage;
}
//! once the Update button is clicked, article resource is updated in this function
void ArticleDetails::UpdateArticle()
{
//! this flag remains false if no new changes are detected, thus avoiding unnecessary
//! re-uploading unchanged resources
bool updated = false;
m_imageId = LoadImage();
if (m_imageId.compare(m_imageIdOriginal) != 0)
{
m_article.SetImageId(m_imageId);
m_filename = "";
m_imageIdOriginal = m_imageId;
m_ui->imagePathLabel->setText(QString("Image: %1").arg(m_imageId));
updated = true;
}
QString newTitle = m_ui->titleText->text();
if (m_article.GetTitle().compare(newTitle))
{
m_article.SetTitle(newTitle);
updated = true;
}
QString newBody = m_ui->descriptionText->toPlainText();
if (m_article.GetBody().compare(newBody) != 0)
{
m_article.SetBody(newBody);
updated = true;
}
QString newStyle = m_ui->articleStyleButtonGroup->checkedButton()->property("style").toString();
if (m_article.GetArticleStyle() != newStyle)
{
m_article.SetArticleStyle(newStyle);
updated = true;
}
if (updated)
{
m_article.Update();
m_manifest.UpdateResource(&m_article.GetResource());
emit logSignal(QString("Article %1 updated")
.arg(m_article.GetResource().GetId()), LogOk);
}
else
{
emit logSignal("Nothing to update", LogWarning);
}
emit updateArticleSignal();
}
//! try to load an image either from filename or from another resource,
//! update resource manifest accordingly
QString ArticleDetails::LoadImage() const
{
if (!m_filename.isEmpty())
{
if (!m_imageIdOriginal.isEmpty())
{
m_manifest.FreeResource(m_imageIdOriginal);
}
auto pResource = m_manifest.AddImage(m_filename);
if (pResource)
{
return pResource->GetId();
}
return "";
}
if (m_imageId.compare(m_imageIdOriginal) != 0)
{
if (!m_imageIdOriginal.isEmpty())
{
m_manifest.FreeResource(m_imageIdOriginal);
}
if (!m_imageId.isEmpty())
{
m_manifest.UseResource(m_imageId);
}
return m_imageId;
}
return m_imageIdOriginal;
}
void ArticleDetails::SetImage(Resource& resource)
{
m_ui->imagePathLabel->setText(QString("Image: %1").arg(resource.GetId()));
QPixmap pixmap;
if (pixmap.loadFromData(resource.GetData()))
{
m_ui->imagePreview->setPixmap(pixmap);
m_imageId = resource.GetId();
}
else
{
SetNoImage();
emit logSignal(QString("Failed to load image: %1.").arg(m_imageId));
}
}
void ArticleDetails::SetImage([[maybe_unused]] QString& filename)
{
m_ui->imagePathLabel->setText(QString("Image: %1").arg(m_filename));
QPixmap pixmap;
if (pixmap.load(m_filename))
{
m_ui->imagePreview->setPixmap(pixmap);
}
else
{
SetNoImage();
emit logSignal(QString("Failed to load image: %1.").arg(m_filename));
}
}
void ArticleDetails::SetNoImage() const
{
m_ui->imagePreview->setPixmap(QPixmap(":/images/Resources/missing-image.png"));
m_ui->imagePathLabel->setText("no image");
}
void ArticleDetails::ClearImage()
{
m_imageId = "";
m_filename = "";
SetNoImage();
}
void ArticleDetails::DeleteArticle()
{
if (!m_article.GetImageId().isEmpty())
{
m_manifest.FreeResource(m_article.GetImageId());
}
m_manifest.FreeResource(m_article.GetResource().GetId());
emit deleteArticleSignal();
}
void ArticleDetails::Close()
{
emit closeArticleSignal();
}
void ArticleDetails::MoveUp()
{
ErrorCode error;
if (!m_manifest.UpdateArticleOrder(m_article.GetResource().GetId(), 1, error))
{
emit logSignal(GetErrorMessage(error));
}
else
{
emit orderChangedSignal(m_article.GetResource().GetId(), 1);
}
}
void ArticleDetails::MoveDown()
{
ErrorCode error;
if (!m_manifest.UpdateArticleOrder(m_article.GetResource().GetId(), 0, error))
{
emit logSignal(GetErrorMessage(error));
}
else
{
emit orderChangedSignal(m_article.GetResource().GetId(), 0);
}
}
#include "Qt/moc_ArticleDetails.cpp"
}
@@ -1,76 +0,0 @@
/*
* 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 <NewsShared/ResourceManagement/ArticleDescriptor.h>
#include "ResourceManagement/BuilderResourceManifest.h"
#include <QWidget>
#endif
namespace Ui
{
class ArticleDetailsWidget;
}
namespace News {
class SelectImage;
class ResourceManifest;
class Resource;
//! Control that allows to modify all parameters of a single article
class ArticleDetails
: public QWidget
{
Q_OBJECT
public:
ArticleDetails(QWidget* parent,
Resource& resource,
BuilderResourceManifest& manifest);
~ArticleDetails();
ArticleDescriptor& GetArticle();
QString GetId() const;
Q_SIGNALS:
void logSignal(QString text, LogType logType = LogInfo);
void deleteArticleSignal();
void updateArticleSignal();
void closeArticleSignal();
void orderChangedSignal(QString id, bool direction);
private:
QScopedPointer<Ui::ArticleDetailsWidget> m_ui;
QString m_filename;
ArticleDescriptor m_article;
BuilderResourceManifest& m_manifest;
SelectImage* m_pSelectImage;
QString m_imageIdOriginal;
QString m_imageId;
float m_imageRatio = 184.0f / 430.0f;
void resizeEvent(QResizeEvent *event) override;
void resizePreviewImage();
void OpenImageFromFile();
void OpenImageFromResource();
QString LoadImage() const;
void SetImage(Resource& resource);
void SetImage(QString& filename);
void SetNoImage() const;
void ClearImage();
void UpdateArticle();
void DeleteArticle();
void Close();
void MoveUp();
void MoveDown();
};
} // namespace News
@@ -1,406 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ArticleDetailsWidget</class>
<widget class="QWidget" name="ArticleDetailsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>250</width>
<height>420</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>420</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>640</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="globalLayout" native="true">
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="AzQtComponents::ExtendedLabel" name="imagePreview">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="newsbuilder.qrc">:/images/Resources/missing-image.png</pixmap>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="pixmapSize" stdset="0">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="imageLayout" native="true">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>140</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="imagePathLabel">
<property name="text">
<string>Image:</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="imageLayoutInner" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="clearImageButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fromResourceButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Library...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fromFileButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Load...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="Title" native="true">
<property name="maximumSize">
<size>
<width>1280</width>
<height>1280</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="uidLabel">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>20</height>
</size>
</property>
<property name="text">
<string>Article: 0</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="articleStyleLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QRadioButton" name="defaultStyleRadioButton">
<property name="text">
<string>Default Style</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="style" stdset="0">
<string>default</string>
</property>
<attribute name="buttonGroup">
<string notr="true">articleStyleButtonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="pinnedStyleRadioButton">
<property name="text">
<string>Pinned Style</string>
</property>
<property name="style" stdset="0">
<string>pinned</string>
</property>
<attribute name="buttonGroup">
<string notr="true">articleStyleButtonGroup</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Title:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="titleText">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="Description">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Description:</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="descriptionText"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="deleteButton">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="updateButton">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="upButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Move Up</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="downButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Move Down</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::ExtendedLabel</class>
<extends>QLabel</extends>
<header>AzQtComponents/Components/ExtendedLabel.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="newsbuilder.qrc"/>
</resources>
<connections/>
<buttongroups>
<buttongroup name="articleStyleButtonGroup"/>
</buttongroups>
</ui>
@@ -1,88 +0,0 @@
/*
* 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 "ArticleDetailsContainer.h"
#include "ResourceManagement/BuilderResourceManifest.h"
#include "ArticleDetails.h"
#include "Qt/ui_ArticleDetailsContainer.h"
namespace News
{
ArticleDetailsContainer::ArticleDetailsContainer(
QWidget* parent,
BuilderResourceManifest& manifest)
: QWidget(parent)
, m_ui(new Ui::ArticleDetailsContainerWidget)
, m_manifest(manifest)
, m_articleDetails(nullptr)
{
m_ui->setupUi(this);
}
ArticleDetailsContainer::~ArticleDetailsContainer() {}
//! When article is selected, old articleDetails control is removed, new one is created
void ArticleDetailsContainer::SelectArticle(const QString& id)
{
closeArticleSlot();
if (!id.isEmpty())
{
auto article = m_manifest.FindById(id);
if (article)
{
m_articleDetails = new ArticleDetails(
m_ui->scrollAreaWidgetContents,
*article,
m_manifest);
m_ui->scrollAreaWidgetContents->layout()->addWidget(m_articleDetails);
connect(m_articleDetails, &ArticleDetails::updateArticleSignal,
this, &ArticleDetailsContainer::updateArticleSlot);
connect(m_articleDetails, &ArticleDetails::deleteArticleSignal,
this, &ArticleDetailsContainer::deleteArticleSlot);
connect(m_articleDetails, &ArticleDetails::closeArticleSignal,
this, &ArticleDetailsContainer::closeArticleSlot);
connect(m_articleDetails, SIGNAL(logSignal(QString, LogType)),
this, SIGNAL(logSignal(QString, LogType)));
connect(m_articleDetails, SIGNAL(orderChangedSignal(QString, bool)),
this, SIGNAL(orderChangedSignal(QString, bool)));
m_selectedId = id;
}
}
}
void ArticleDetailsContainer::Reset()
{
SelectArticle(m_selectedId);
}
void ArticleDetailsContainer::updateArticleSlot()
{
emit updateArticleSignal(m_articleDetails->GetId());
}
void ArticleDetailsContainer::deleteArticleSlot()
{
emit deleteArticleSignal(m_articleDetails->GetId());
closeArticleSlot();
}
void ArticleDetailsContainer::closeArticleSlot()
{
if (m_articleDetails)
{
emit closeArticleSignal(m_selectedId);
delete m_articleDetails;
m_articleDetails = nullptr;
m_selectedId = "";
}
}
#include "Qt/moc_ArticleDetailsContainer.cpp"
}
@@ -1,59 +0,0 @@
/*
* 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 <QWidget>
#include <NewsShared/LogType.h>
#endif
namespace Ui
{
class ArticleDetailsContainerWidget;
}
namespace News
{
class ArticleDetails;
class BuilderResourceManifest;
class SelectImage;
class ResourceManifest;
class Resource;
//! Control that manages articleDetails control
class ArticleDetailsContainer
: public QWidget
{
Q_OBJECT
public:
ArticleDetailsContainer(QWidget* parent, BuilderResourceManifest& manifest);
~ArticleDetailsContainer();
void SelectArticle(const QString& id);
void Reset();
Q_SIGNALS:
void logSignal(QString text, LogType logType = LogInfo);
void updateArticleSignal(QString id);
void deleteArticleSignal(QString id);
void closeArticleSignal(QString id);
void orderChangedSignal(QString id, bool direction);
private:
QScopedPointer<Ui::ArticleDetailsContainerWidget> m_ui;
BuilderResourceManifest& m_manifest;
ArticleDetails* m_articleDetails = nullptr;
QString m_selectedId;
private Q_SLOTS:
void updateArticleSlot();
void deleteArticleSlot();
void closeArticleSlot();
};
} // namespace News
@@ -1,100 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ArticleDetailsContainerWidget</class>
<widget class="QWidget" name="ArticleDetailsContainerWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>250</width>
<height>386</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>640</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>ArticleDetailsContainer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>248</width>
<height>384</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
-102
View File
@@ -1,102 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>176</width>
<height>137</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>AWS Key Id:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="textKeyId"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>AWS Secret Key:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="textKeyId_2"/>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
@@ -1,157 +0,0 @@
/*
* 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 "BuilderArticleViewContainer.h"
#include "ResourceManagement/BuilderResourceManifest.h"
#include "ArticleDetails.h"
#include <NewsShared/ResourceManagement/Resource.h>
#include <NewsShared/Qt/ArticleViewContainer.h>
#include <NewsShared/Qt/ArticleView.h>
#include "Qt/ui_BuilderArticleViewContainer.h"
#include <AzQtComponents/Components/Style.h>
#include "QCustomMessageBox.h"
namespace News
{
BuilderArticleViewContainer::BuilderArticleViewContainer(
QWidget* parent,
BuilderResourceManifest& manifest)
: QWidget(parent)
, m_ui(new Ui::BuilderArticleViewContainerWidget)
, m_container(new ArticleViewContainer(this, manifest))
, m_manifest(manifest)
{
m_ui->setupUi(this);
m_ui->ArticleViewContainerRoot->layout()->addWidget(m_container);
connect(m_container, SIGNAL(articleSelectedSignal(QString)),
this, SLOT(articleSelectedSlot(QString)));
}
BuilderArticleViewContainer::~BuilderArticleViewContainer(){}
void BuilderArticleViewContainer::AddArticle()
{
auto pArticleResource = static_cast<BuilderResourceManifest&>(m_manifest).AddArticle();
if (pArticleResource)
{
m_container->AddArticleView(ArticleDescriptor(*pArticleResource));
SelectArticle(pArticleResource->GetId());
}
}
void BuilderArticleViewContainer::SelectArticle(QString id)
{
if (m_selectedArticleId.compare(id) == 0)
{
return;
}
UnselectArticle();
m_selectedArticleId = id;
auto article = m_container->FindById(id);
if (article)
{
m_container->ScrollToView(article);
emit articleSelectedSignal(id);
// Add class and refresh style
AzQtComponents::Style::addClass(article, m_selectedArticleClass);
article->style()->unpolish(qApp);
article->style()->polish(qApp);
}
}
void BuilderArticleViewContainer::Sync()
{
emit logSignal("Starting sync");
m_manifest.Sync();
}
void BuilderArticleViewContainer::UpdateArticle(QString& id) const
{
auto view = m_container->FindById(id);
if (view)
{
view->Update();
m_container->ForceRefreshArticleView(view);
}
}
void BuilderArticleViewContainer::DeleteArticle(QString& id) const
{
auto view = m_container->FindById(id);
if (view)
{
m_container->DeleteArticleView(view);
}
}
//possibly support multiple selection in future?
void BuilderArticleViewContainer::CloseArticle(QString& id)
{
if (m_selectedArticleId == id)
{
UnselectArticle();
}
}
void BuilderArticleViewContainer::UpdateArticleOrder(QString& id, bool direction) const
{
auto view = m_container->FindById(id);
if (view)
{
m_container->UpdateArticleOrder(view, direction);
}
}
void BuilderArticleViewContainer::PopulateArticles()
{
m_container->PopulateArticles();
if (!m_selectedArticleId.isEmpty())
{
SelectArticle(m_selectedArticleId);
}
}
void BuilderArticleViewContainer::AddErrorMessage() const
{
m_container->AddErrorMessage();
}
void BuilderArticleViewContainer::UnselectArticle()
{
if (m_selectedArticleId.isEmpty())
{
return;
}
auto article = m_container->FindById(m_selectedArticleId);
// Remove class and refresh style
if (article)
{
AzQtComponents::Style::removeClass(article, m_selectedArticleClass);
article->style()->unpolish(qApp);
article->style()->polish(qApp);
}
m_selectedArticleId = "";
}
void BuilderArticleViewContainer::articleSelectedSlot(QString id)
{
SelectArticle(id);
}
#include "Qt/moc_BuilderArticleViewContainer.cpp"
}
@@ -1,64 +0,0 @@
/*
* 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 <QWidget>
#include <NewsShared/LogType.h>
#endif
namespace Ui
{
class BuilderArticleViewContainerWidget;
}
namespace News
{
class ArticleViewContainer;
class BuilderResourceManifest;
//! BuilderArticleViewContainer is a Builder container for ArticleViewContainer.
/*!
It is a wrapper with additional builder functionality for displaying articles
*/
class BuilderArticleViewContainer
: public QWidget
{
Q_OBJECT
public:
BuilderArticleViewContainer(QWidget* parent, BuilderResourceManifest& manifest);
~BuilderArticleViewContainer();
void UpdateArticle(QString& id) const;
void DeleteArticle(QString& id) const;
void CloseArticle(QString& id);
void UpdateArticleOrder(QString& id, bool direction) const;
void PopulateArticles();
void AddErrorMessage() const;
void AddArticle();
Q_SIGNALS:
void logSignal(QString text, LogType logType = LogInfo);
void articleSelectedSignal(QString id);
private:
QScopedPointer<Ui::BuilderArticleViewContainerWidget> m_ui;
ArticleViewContainer* m_container;
QString m_selectedArticleId;
QString m_selectedArticleClass = "SelectedArticle";
BuilderResourceManifest& m_manifest;
void Sync();
void SelectArticle(QString id);
void UnselectArticle();
private Q_SLOTS:
void articleSelectedSlot(QString id);
};
} // namespace News
@@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BuilderArticleViewContainerWidget</class>
<widget class="QWidget" name="BuilderArticleViewContainerWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>250</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="ArticleViewContainerRoot" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -1,58 +0,0 @@
/*
* 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 "EndpointEntryView.h"
#include "Qt/ui_EndpointEntryView.h"
#include <EndpointManager.h>
#include <QToolButton>
namespace News
{
const char* EndpointEntryView::SELECTED_CSS =
"background-color: rgb(60, 100, 60);\ncolor: white;";
const char* EndpointEntryView::UNSELECTED_CSS =
"background-color: rgb(60, 60, 60);\ncolor: white;";
EndpointEntryView::EndpointEntryView(QWidget* parent, Endpoint* pEndpoint)
: QWidget(parent)
, m_ui(new Ui::EndpointEntryViewWidget)
, m_pEndpoint(pEndpoint)
{
m_ui->setupUi(this);
m_ui->labelName->setText(pEndpoint->GetName());
connect(m_ui->labelName, &AzQtComponents::ExtendedLabel::clicked, this, &EndpointEntryView::selectSlot);
connect(m_ui->buttonDelete, &QToolButton::clicked, this, &EndpointEntryView::deleteSlot);
}
EndpointEntryView::~EndpointEntryView() {}
void EndpointEntryView::SetSelected(bool selected)
{
setStyleSheet(selected ? SELECTED_CSS : UNSELECTED_CSS);
}
Endpoint* EndpointEntryView::GetEndpoint() const
{
return m_pEndpoint;
}
void EndpointEntryView::selectSlot()
{
emit selectSignal(this);
}
void EndpointEntryView::deleteSlot()
{
emit deleteSignal(this);
}
#include "Qt/moc_EndpointEntryView.cpp"
}
@@ -1,51 +0,0 @@
/*
* 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 <QWidget>
#endif
namespace Ui
{
class EndpointEntryViewWidget;
}
namespace News
{
class Endpoint;
//! Qt widget representing a single endpoint entry
class EndpointEntryView
: public QWidget
{
Q_OBJECT
public:
EndpointEntryView(QWidget* parent, Endpoint* pEndpoint);
~EndpointEntryView();
void SetSelected(bool selected);
Endpoint* GetEndpoint() const;
Q_SIGNALS:
void selectSignal(EndpointEntryView* pEndpointView);
void deleteSignal(EndpointEntryView* pEndpointView);
private:
static const char* SELECTED_CSS;
static const char* UNSELECTED_CSS;
QScopedPointer<Ui::EndpointEntryViewWidget> m_ui;
Endpoint* m_pEndpoint = nullptr;
private Q_SLOTS:
void selectSlot();
void deleteSlot();
};
} // namespace News
@@ -1,87 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EndpointEntryViewWidget</class>
<widget class="QWidget" name="EndpointEntryViewWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>356</width>
<height>23</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>23</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>23</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="AzQtComponents::ExtendedLabel" name="labelName">
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonDelete">
<property name="minimumSize">
<size>
<width>25</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: red;</string>
</property>
<property name="text">
<string>X</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::ExtendedLabel</class>
<extends>QLabel</extends>
<header>AzQtComponents/Components/ExtendedLabel.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -1,221 +0,0 @@
/*
* 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 "EndpointManagerView.h"
#include "EndpointManager.h"
#include "EndpointEntryView.h"
#include "Qt/ui_EndpointManagerView.h"
#include "Qt/QCustomMessageBox.h"
#include "ResourceManagement/BuilderResourceManifest.h"
#include <QMessageBox>
namespace News
{
EndpointManagerView::EndpointManagerView(QWidget* parent,
BuilderResourceManifest& manifest)
: QDialog(parent)
, m_ui(new Ui::EndpointManagerViewWidget)
, m_pManager(manifest.GetEndpointManager())
, m_manifest(manifest)
{
m_ui->setupUi(this);
for (auto endpoint : *m_pManager)
{
auto endpointView = AddEndpointEntry(endpoint);
if (m_pManager->GetSelectedEndpoint() == endpoint)
{
selectEndpointSlot(endpointView);
}
}
connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(m_ui->buttonAdd, &QPushButton::clicked, this, &EndpointManagerView::addEndpointSlot);
}
EndpointManagerView::~EndpointManagerView() {}
EndpointEntryView* EndpointManagerView::AddEndpointEntry(Endpoint* pEndpoint)
{
auto endpointView = new EndpointEntryView(m_ui->endpointListContents, pEndpoint);
auto layout = static_cast<QVBoxLayout*>(m_ui->endpointListContents->layout());
layout->insertWidget(layout->count() - 2, endpointView);
m_endpoints.append(endpointView);
connect(endpointView, SIGNAL(selectSignal(EndpointEntryView*)),
this, SLOT(selectEndpointSlot(EndpointEntryView*)));
connect(endpointView, SIGNAL(deleteSignal(EndpointEntryView*)),
this, SLOT(deleteEndpointSlot(EndpointEntryView*)));
// this lets us modify newly-added widget in the same function
qApp->processEvents();
return endpointView;
}
void EndpointManagerView::Update() const
{
if (m_pSelectedEndpoint)
{
auto pEndpoint = m_pSelectedEndpoint->GetEndpoint();
pEndpoint->SetName(m_ui->nameText->text());
pEndpoint->SetAwsProfile(m_ui->awsProfileText->text());
pEndpoint->SetUrl(m_ui->urlText->text());
pEndpoint->SetBucket(m_ui->bucketText->text());
m_pManager->SelectEndpoint(m_pSelectedEndpoint->GetEndpoint());
}
m_pManager->Save();
}
void EndpointManagerView::accept()
{
enum SyncResponse
{
Merge, ReplaceLocal, ReplaceRemote, Cancel
};
enum YesNoResponse
{
Yes, No
};
QCustomMessageBox msgBox(
QCustomMessageBox::Question,
tr("Pull data from Endpoint"),
tr("You are changing an endpoint. "
"What would you like to do with the data on the current endpoint?\n\n"
"Merge - merge resources from the endpoint\n"
"Replace Local - overwrite local resources with endpoint resources\n"
"Replace Endpoint - overwrite endpoint resources with local resources\n"
"Cancel - undo endpoint selection"
),
this);
msgBox.AddButton(tr("Merge"), Merge);
msgBox.AddButton(tr("Replace Local"), ReplaceLocal);
msgBox.AddButton(tr("Replace Endpoint"), ReplaceRemote);
msgBox.AddButton(tr("Cancel"), Cancel);
switch (msgBox.exec())
{
case Merge:
{
m_manifest.SetSyncType(SyncType::Merge);
m_manifest.PersistLocalResources();
}
break;
case ReplaceLocal:
{
if (m_manifest.HasChanges())
{
QCustomMessageBox msgBoxWarning(
QCustomMessageBox::Critical,
tr("Unsaved changes"),
tr("Local resources were modified but not published."
"Changing endpoints will cause unpublished work to be lost.\n\n"
"Would you like to proceed?"),
this);
msgBoxWarning.AddButton(tr("Yes"), Yes);
msgBoxWarning.AddButton(tr("No"), No);
if (msgBoxWarning.exec() == No)
{
return;
}
}
m_manifest.SetSyncType(SyncType::Merge);
m_manifest.Reset();
}
break;
case ReplaceRemote:
{
QCustomMessageBox msgBoxWarning2(
QCustomMessageBox::Critical,
tr("Warning"),
tr("This operation will IRREVERSABLY replace ALL resources on ") +
m_pSelectedEndpoint->GetEndpoint()->GetName() +
tr(" endpoint with local data.\n\n"
"Are you sure you'd like to proceed?"),
this);
msgBoxWarning2.AddButton(tr("Yes"), Yes);
msgBoxWarning2.AddButton(tr("No"), No);
if (msgBoxWarning2.exec() == No)
{
return;
}
m_manifest.SetSyncType(SyncType::Overwrite);
m_manifest.PersistLocalResources();
}
break;
case Cancel:
return;
}
Update();
QDialog::accept();
}
void EndpointManagerView::addEndpointSlot()
{
auto pEndpoint = new Endpoint(
QObject::tr("New endpoint"),
QObject::tr("Enter AWS profile name"),
QObject::tr("Enter root URL"),
QObject::tr("Enter s3 bucket name"));
m_pManager->AddEndpoint(pEndpoint);
auto endpointView = AddEndpointEntry(pEndpoint);
selectEndpointSlot(endpointView);
}
void EndpointManagerView::selectEndpointSlot(EndpointEntryView* pEndpointView)
{
m_pSelectedEndpoint = pEndpointView;
for (auto view : m_endpoints)
{
view->SetSelected(view == pEndpointView);
}
if (pEndpointView)
{
auto pEndpoint = pEndpointView->GetEndpoint();
m_ui->nameText->setText(pEndpoint->GetName());
m_ui->awsProfileText->setText(pEndpoint->GetAwsProfile());
m_ui->urlText->setText(pEndpoint->GetUrl());
m_ui->bucketText->setText(pEndpoint->GetBucket());
}
else
{
m_ui->nameText->setText("");
m_ui->awsProfileText->setText("");
m_ui->urlText->setText("");
m_ui->bucketText->setText("");
}
}
void EndpointManagerView::deleteEndpointSlot(EndpointEntryView* pEndpointView)
{
m_pManager->RemoveEndpoint(pEndpointView->GetEndpoint());
m_endpoints.removeAll(pEndpointView);
if (pEndpointView == m_pSelectedEndpoint)
{
if (m_endpoints.count() > 0)
{
selectEndpointSlot(m_endpoints[0]);
}
else
{
selectEndpointSlot(nullptr);
}
}
delete pEndpointView;
}
#include "Qt/moc_EndpointManagerView.cpp"
}
@@ -1,52 +0,0 @@
/*
* 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 <QDialog>
#endif
namespace Ui
{
class EndpointManagerViewWidget;
}
namespace News
{
class BuilderResourceManifest;
class Endpoint;
class EndpointEntryView;
class EndpointManager;
//! Qt dialog for managing endpoints
class EndpointManagerView
: public QDialog
{
Q_OBJECT
public:
EndpointManagerView(QWidget* parent,
BuilderResourceManifest& manifest);
~EndpointManagerView();
private:
QScopedPointer<Ui::EndpointManagerViewWidget> m_ui;
EndpointManager* m_pManager = nullptr;
EndpointEntryView* m_pSelectedEndpoint = nullptr;
QList<EndpointEntryView*> m_endpoints;
BuilderResourceManifest& m_manifest;
EndpointEntryView* AddEndpointEntry(Endpoint* pEndpoint);
void Update() const;
private Q_SLOTS:
void accept() override;
void addEndpointSlot();
void selectEndpointSlot(EndpointEntryView* pEndpoint);
void deleteEndpointSlot(EndpointEntryView* pEndpoint);
};
} // namespace News
@@ -1,224 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EndpointManagerViewWidget</class>
<widget class="QDialog" name="EndpointManagerViewWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>350</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>350</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>400</width>
<height>350</height>
</size>
</property>
<property name="windowTitle">
<string>Change Endpoint</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="endpointListContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>363</width>
<height>189</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QPushButton" name="buttonAdd">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="nameText">
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_1">
<property name="text">
<string>AWS Profile Name:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="awsProfileText">
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>URL:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="urlText">
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>S3 Bucket</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="bucketText">
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -1,54 +0,0 @@
/*
* 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 "ImageItem.h"
#include "NewsShared/ResourceManagement/Resource.h"
#include "Qt/ui_ImageItem.h"
namespace News
{
const char* ImageItem::SELECTED_CSS =
"border: 4px solid; border-color: white; background-color: rgb(45, 45, 45);";
const char* ImageItem::UN_SELECTED_CSS =
"background-color: rgb(45, 45, 45);";
ImageItem::ImageItem(Resource& resource)
: QWidget()
, m_ui(new Ui::ImageItemWidget)
, m_resource(resource)
{
m_ui->setupUi(this);
QPixmap pixmap;
pixmap.loadFromData(m_resource.GetData());
m_ui->ImageLabel->setPixmap(pixmap);
connect(m_ui->ImageLabel, &AzQtComponents::ExtendedLabel::clicked, this, &ImageItem::imageClickedSlot);
}
void ImageItem::imageClickedSlot()
{
emit selectSignal(this);
}
ImageItem::~ImageItem() {}
void ImageItem::SetSelect(bool selected) const
{
m_ui->ImageLabel->setStyleSheet(selected ? SELECTED_CSS : UN_SELECTED_CSS);
}
Resource& ImageItem::GetResource() const
{
return m_resource;
}
#include "Qt/moc_ImageItem.cpp"
}
@@ -1,46 +0,0 @@
/*
* 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 <QWidget>
#endif
namespace Ui
{
class ImageItemWidget;
}
namespace News {
class Resource;
//! A clickable image in selectImage control
class ImageItem
: public QWidget
{
Q_OBJECT
public:
explicit ImageItem(Resource& resource);
~ImageItem();
void SetSelect(bool selected) const;
Resource& GetResource() const;
Q_SIGNALS:
void selectSignal(ImageItem* imageItem);
private:
QScopedPointer<Ui::ImageItemWidget> m_ui;
const static char* SELECTED_CSS;
const static char* UN_SELECTED_CSS;
Resource& m_resource;
private Q_SLOTS:
void imageClickedSlot();
};
}
@@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ImageItemWidget</class>
<widget class="QWidget" name="ImageItemWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>200</width>
<height>200</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>20</number>
</property>
<property name="topMargin">
<number>20</number>
</property>
<property name="rightMargin">
<number>20</number>
</property>
<property name="bottomMargin">
<number>20</number>
</property>
<item>
<widget class="AzQtComponents::ExtendedLabel" name="ImageLabel">
<property name="text">
<string/>
</property>
<property name="pixmapSize" stdset="0">
<size>
<width>160</width>
<height>90</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::ExtendedLabel1</class>
<extends>QLabel</extends>
<header>AzQtComponents/Components/ExtendedLabel.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -1,51 +0,0 @@
/*
* 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 "LogContainer.h"
#include "Qt/ui_LogContainer.h"
namespace News
{
LogContainer::LogContainer(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::LogContainerWidget)
{
m_ui->setupUi(this);
}
LogContainer::~LogContainer() {}
void LogContainer::AddLog(QString text, LogType logType) const {
QString color;
switch (logType)
{
case LogOk:
color = "green";
break;
case LogInfo:
color = "white";
break;
case LogError:
color = "red";
break;
case LogWarning:
color = "yellow";
break;
default:
color = "white";
break;
}
auto fullText = QString("<span style=\" color:%1; \"><XMP>%2</XMP></span><br>").arg(color).arg(text);
m_ui->logText->setTextFormat(Qt::RichText);
m_ui->logText->setText(QString("%2%1").arg(m_ui->logText->text()).arg(fullText));
}
#include "Qt/moc_LogContainer.cpp"
}
@@ -1,37 +0,0 @@
/*
* 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 <QWidget>
#include <NewsShared/LogType.h>
#endif
namespace Ui
{
class LogContainerWidget;
}
namespace News
{
//! A control for displaying log
class LogContainer
: public QWidget
{
Q_OBJECT
public:
explicit LogContainer(QWidget* parent);
~LogContainer();
void AddLog(QString text, LogType logType) const;
private:
QScopedPointer<Ui::LogContainerWidget> m_ui;
};
} // namespace News
@@ -1,131 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LogContainerWidget</class>
<widget class="QWidget" name="LogContainerWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>547</width>
<height>125</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>LogContainer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="logArea_3">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>545</width>
<height>123</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="logText">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(45, 45, 45);</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>false</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -1,243 +0,0 @@
/*
* 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 <AzCore/PlatformDef.h>
#include <AzCore/IO/Path/Path.h>
#include "NewsBuilder.h"
#include "Qt/ArticleDetails.h"
#include "Qt/BuilderArticleViewContainer.h"
#include "NewsShared/Qt/ArticleViewContainer.h"
#include "NewsShared/ResourceManagement/Resource.h"
#include "ResourceManagement/BuilderResourceManifest.h"
#include "ArticleDetailsContainer.h"
#include "LogContainer.h"
#include "EndpointManagerView.h"
#include "EndpointManager.h"
#include "Qt/QCustomMessageBox.h"
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Utilities/QtPluginPaths.h>
AZ_PUSH_DISABLE_WARNING(4251 4996, "-Wunknown-warning-option")
#include <aws/core/utils/HashingUtils.h>
AZ_POP_DISABLE_WARNING
#include <QImage>
#include <QFileDialog>
#include <QSignalMapper>
#include <QTextDocument>
#include "Qt/ui_Newsbuilder.h"
namespace News
{
NewsBuilder::NewsBuilder(QWidget* parent, const AZ::IO::PathView& engineRootPath)
: QMainWindow(parent)
, m_ui(new Ui::NewsBuilderClass())
, m_manifest(new BuilderResourceManifest(
std::bind(&NewsBuilder::SyncSuccess, this),
std::bind(&NewsBuilder::SyncFail, this, std::placeholders::_1),
std::bind(&NewsBuilder::SyncUpdate, this, std::placeholders::_1, std::placeholders::_2)))
, m_articleDetailsContainer(new ArticleDetailsContainer(this, *m_manifest))
, m_articleViewContainer(new BuilderArticleViewContainer(this, *m_manifest))
, m_logContainer(new LogContainer(this))
{
AzQtComponents::StyleManager* m_styleSheet = new AzQtComponents::StyleManager(this);
m_styleSheet->initialize(qApp, engineRootPath);
m_ui->setupUi(this);
QDir rootDir = QString::fromUtf8(engineRootPath.Native().data(), aznumeric_cast<int>(engineRootPath.Native().size()));
const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/News/NewsBuilder/Resources");
const auto qrcPath = QStringLiteral(":/NewsBuilder");
AzQtComponents::StyleManager::addSearchPaths("newsbuilder", pathOnDisk, qrcPath, engineRootPath);
AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("newsbuilder:NewsBuilder.qss"));
UpdateEndpointLabel();
m_ui->articleViewContainerRoot->layout()->addWidget(m_articleViewContainer);
m_ui->articleDetailsContainerRoot->layout()->addWidget(m_articleDetailsContainer);
m_ui->dockWidgetContents->layout()->addWidget(m_logContainer);
connect(m_articleViewContainer, SIGNAL(articleSelectedSignal(QString)),
this, SLOT(selectArticleSlot(QString)));
connect(m_articleViewContainer, SIGNAL(logSignal(QString, LogType)),
this, SLOT(addLogSlot(QString, LogType)));
connect(m_articleDetailsContainer, &ArticleDetailsContainer::updateArticleSignal,
this, &NewsBuilder::updateArticleSlot);
connect(m_articleDetailsContainer, &ArticleDetailsContainer::deleteArticleSignal,
this, &NewsBuilder::deleteArticleSlot);
connect(m_articleDetailsContainer, &ArticleDetailsContainer::orderChangedSignal,
this, &NewsBuilder::orderChangedSlot);
connect(m_articleDetailsContainer, &ArticleDetailsContainer::closeArticleSignal,
this, &NewsBuilder::closeArticleSlot);
connect(m_articleDetailsContainer, SIGNAL(logSignal(QString, LogType)),
this, SLOT(addLogSlot(QString, LogType)));
m_manifest->SetSyncType(SyncType::Merge);
m_manifest->Sync();
// Sync Console pane with View menu
connect(m_ui->actionConsole, &QAction::triggered, this, &NewsBuilder::OnViewLogWindow);
connect(m_ui->dockWidget, &QDockWidget::visibilityChanged, this, &NewsBuilder::OnViewVisibilityChanged);
}
NewsBuilder::~NewsBuilder() {}
void NewsBuilder::selectArticleSlot(const QString& id) const
{
m_articleDetailsContainer->SelectArticle(id);
}
void NewsBuilder::addLogSlot(QString text, LogType logType) const
{
AddLog(text, logType);
}
void NewsBuilder::addArticleToBottomSlot() const
{
m_articleViewContainer->AddArticle();
}
void NewsBuilder::updateArticleSlot(QString id) const
{
m_articleViewContainer->UpdateArticle(id);
}
void NewsBuilder::deleteArticleSlot(QString id) const
{
m_articleViewContainer->DeleteArticle(id);
}
void NewsBuilder::closeArticleSlot(QString id) const
{
m_articleViewContainer->CloseArticle(id);
}
void NewsBuilder::orderChangedSlot(QString id, bool direction) const
{
m_articleViewContainer->UpdateArticleOrder(id, direction);
}
void NewsBuilder::openSlot()
{
EndpointManagerView endpointManagerView(
m_ui->centralWidget,
*m_manifest);
if (endpointManagerView.exec() == QDialog::Accepted)
{
m_manifest->Sync();
}
UpdateEndpointLabel();
}
void NewsBuilder::publishSlot()
{
if (!m_manifest->HasChanges())
{
QCustomMessageBox msgBox(
QCustomMessageBox::Information,
tr("Nothing to publish"),
tr("No local changes were made, nothing to publish."),
this);
msgBox.AddButton(tr("Good"), 0);
msgBox.exec();
return;
}
enum Response
{
Yes, No
};
QCustomMessageBox msgBox(
QCustomMessageBox::Critical,
tr("Publish resources"),
tr("You are about to overwrite the current Open 3D Engine Welcome Message. Are you sure you want to publish?"),
this);
msgBox.AddButton("Yes", Yes);
msgBox.AddButton("No", No);
if (msgBox.exec() == Yes)
{
m_manifest->SetSyncType(SyncType::Verify);
m_manifest->Sync();
}
}
void NewsBuilder::OnViewVisibilityChanged([[maybe_unused]] bool visibility)
{
UpdateViewMenu();
}
void NewsBuilder::UpdateViewMenu()
{
if (m_ui->actionConsole->isChecked() != m_ui->dockWidget->isVisible())
{
QSignalBlocker signalBlocker(m_ui->actionConsole);
m_ui->actionConsole->setChecked(m_ui->dockWidget->isVisible());
}
}
void NewsBuilder::OnViewLogWindow()
{
if (m_ui->dockWidget)
{
m_ui->dockWidget->toggleViewAction()->trigger();
}
}
void NewsBuilder::UpdateEndpointLabel()
{
auto pEndpoint = m_manifest->GetEndpointManager()->GetSelectedEndpoint();
if (pEndpoint)
{
this->setWindowTitle("News Builder (" + pEndpoint->GetName() + ")");
}
else
{
this->setWindowTitle("News Builder (No endpoint selected)");
}
}
void NewsBuilder::AddLog(QString text, LogType logType) const
{
m_logContainer->AddLog(text, logType);
}
void NewsBuilder::SyncUpdate(const QString& message, LogType logType) const
{
AddLog(message, logType);
}
void NewsBuilder::SyncFail(ErrorCode error)
{
const char* errorMessage = GetErrorMessage(error);
AddLog(tr("Sync failed: %1").arg(errorMessage), LogError);
QCustomMessageBox msgBoxSyncFail(
QCustomMessageBox::Critical,
tr("Sync failed"),
errorMessage,
this);
msgBoxSyncFail.AddButton("Ok", 0);
msgBoxSyncFail.exec();
m_articleViewContainer->PopulateArticles();
m_articleDetailsContainer->Reset();
}
void NewsBuilder::SyncSuccess() const
{
AddLog("Sync completed", LogOk);
m_articleViewContainer->PopulateArticles();
m_articleDetailsContainer->Reset();
}
#include "Qt/moc_NewsBuilder.cpp"
}
@@ -1,73 +0,0 @@
/*
* 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 <QMainWindow>
#include "NewsShared/LogType.h"
#include "NewsShared/ErrorCodes.h"
#include <AzCore/IO/Path/Path_fwd.h>
#endif
class QSignalMapper;
class QPushButton;
namespace Ui
{
class NewsBuilderClass;
}
namespace News
{
class BuilderArticleViewContainer;
class LogContainer;
class BuilderResourceManifest;
class ArticleDetailsContainer;
//! A central control of News Builder.
class NewsBuilder
: public QMainWindow
{
Q_OBJECT
public:
explicit NewsBuilder(QWidget* parent, const AZ::IO::PathView& engineRootPath);
~NewsBuilder();
private:
QScopedPointer<Ui::NewsBuilderClass> m_ui;
BuilderResourceManifest* m_manifest = nullptr;
ArticleDetailsContainer* m_articleDetailsContainer = nullptr;
BuilderArticleViewContainer* m_articleViewContainer = nullptr;
LogContainer* m_logContainer = nullptr;
void UpdateEndpointLabel();
void AddLog(QString text, LogType logType = LogInfo) const;
void SyncUpdate(const QString& message, LogType logType) const;
void SyncFail(ErrorCode error);
void SyncSuccess() const;
void OnViewVisibilityChanged(bool visibility);
void UpdateViewMenu();
void OnViewLogWindow();
private Q_SLOTS:
void selectArticleSlot(const QString& id) const;
void addLogSlot(QString text, LogType logType) const;
void addArticleToBottomSlot() const;
void updateArticleSlot(QString id) const;
void deleteArticleSlot(QString id) const;
void closeArticleSlot(QString id) const;
void orderChangedSlot(QString id, bool direction) const;
void openSlot();
void publishSlot();
};
} // namespace News
@@ -1,5 +0,0 @@
<RCC>
<qresource prefix="images">
<file>../Resources/missing-image.png</file>
</qresource>
</RCC>
@@ -1,326 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewsBuilderClass</class>
<widget class="QMainWindow" name="NewsBuilderClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>540</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>480</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1024</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>News Builder</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="verticalLayout1" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>800</width>
<height>0</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="articleViewContainerRoot" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>480</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="articleDetailsContainerRoot" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionOpen"/>
<addaction name="actionPublish"/>
</widget>
<widget class="QMenu" name="menuArticle">
<property name="title">
<string>Article</string>
</property>
<addaction name="actionAdd_New"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
<addaction name="actionConsole"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuArticle"/>
<addaction name="menuView"/>
</widget>
<widget class="AzQtComponents::StyledDockWidget" name="dockWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>63</width>
<height>66</height>
</size>
</property>
<property name="features">
<set>QDockWidget::DockWidgetClosable</set>
</property>
<property name="allowedAreas">
<set>Qt::BottomDockWidgetArea</set>
</property>
<property name="windowTitle">
<string>Console</string>
</property>
<attribute name="dockWidgetArea">
<number>8</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</widget>
<action name="actionOpen">
<property name="text">
<string>Change Endpoint</string>
</property>
<property name="autoRepeat">
<bool>false</bool>
</property>
</action>
<action name="actionPublish">
<property name="text">
<string>Publish Resources</string>
</property>
<property name="autoRepeat">
<bool>false</bool>
</property>
</action>
<action name="actionAdd_New">
<property name="text">
<string>Add to Bottom</string>
</property>
</action>
<action name="actionDelete_Selected">
<property name="text">
<string>Delete Selected</string>
</property>
</action>
<action name="actionConsole">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Console</string>
</property>
</action>
</widget>
<layoutdefault spacing="0" margin="0"/>
<customwidgets>
<customwidget>
<class>AzQtComponents::StyledDockWidget</class>
<extends>QDockWidget</extends>
<header>AzQtComponents/Components/StyledDockWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="newsbuilder.qrc"/>
</resources>
<connections>
<connection>
<sender>actionOpen</sender>
<signal>triggered()</signal>
<receiver>NewsBuilderClass</receiver>
<slot>openSlot()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>269</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionPublish</sender>
<signal>triggered()</signal>
<receiver>NewsBuilderClass</receiver>
<slot>publishSlot()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>269</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionAdd_New</sender>
<signal>triggered()</signal>
<receiver>NewsBuilderClass</receiver>
<slot>addArticleToBottomSlot()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>269</y>
</hint>
</hints>
</connection>
</connections>
</ui>
@@ -1,82 +0,0 @@
/*
* 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 "QCustomMessageBox.h"
#include "Qt/ui_QCustomMessageBox.h"
#include <QSignalMapper>
#include <QPushButton>
#include <QStyle>
namespace News
{
QCustomMessageBox::QCustomMessageBox(
Icon icon,
const QString& title,
const QString& text,
QWidget* parent)
: QDialog(parent, Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint)
, m_ui(new Ui::CustomMessageBoxDialog)
{
m_ui->setupUi(this);
m_signalMapper = new QSignalMapper(this);
this->setWindowTitle(title);
m_ui->labelText->setText(text);
QStyle* style = QApplication::style();
QIcon tmpIcon;
switch (icon)
{
case Information:
tmpIcon = style->standardIcon(QStyle::SP_MessageBoxInformation);
break;
case Warning:
tmpIcon = style->standardIcon(QStyle::SP_MessageBoxWarning);
break;
case Critical:
tmpIcon = style->standardIcon(QStyle::SP_MessageBoxCritical);
break;
case Question:
tmpIcon = style->standardIcon(QStyle::SP_MessageBoxQuestion);
default:
break;
}
if (!tmpIcon.isNull())
{
QLabel* iconLabel = new QLabel(this);
iconLabel->setPixmap(tmpIcon.pixmap(64, 64));
m_ui->bodyLayout->insertWidget(0, iconLabel);
}
connect(m_signalMapper, SIGNAL(mapped(int)),
this, SLOT(clickedSlot(int)));
}
QCustomMessageBox::~QCustomMessageBox()
{
delete m_signalMapper;
}
void QCustomMessageBox::AddButton(const QString& name, int result)
{
auto button = new QPushButton(name, this);
connect(button, SIGNAL(clicked()), m_signalMapper, SLOT(map()));
m_signalMapper->setMapping(button, result);
m_ui->buttonLayout->layout()->addWidget(button);
}
void QCustomMessageBox::clickedSlot(int result)
{
done(result);
}
#include "Qt/moc_QCustomMessageBox.cpp"
}
@@ -1,60 +0,0 @@
/*
* 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 <QDialog>
#endif
class QSignalMapper;
namespace Ui
{
class CustomMessageBoxDialog;
}
namespace News
{
class ArticleDetails;
class BuilderResourceManifest;
class SelectImage;
class ResourceManifest;
class Resource;
//! Allows to add custom buttons which is not well-supported by default QMessageBox
class QCustomMessageBox
: public QDialog
{
Q_OBJECT
public:
enum Icon {
NoIcon = 0,
Information = 1,
Warning = 2,
Critical = 3,
Question = 4
};
QCustomMessageBox(
Icon,
const QString& title,
const QString& text,
QWidget* parent);
~QCustomMessageBox();
void AddButton(const QString& name, int result);
private:
QScopedPointer<Ui::CustomMessageBoxDialog> m_ui;
QSignalMapper* m_signalMapper = nullptr;
private Q_SLOTS:
void clickedSlot(int result);
};
} // namespace News
@@ -1,84 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CustomMessageBoxDialog</class>
<widget class="QDialog" name="CustomMessageBoxDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>102</width>
<height>43</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Message Title</string>
</property>
<property name="whatsThis">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="bodyLayout">
<item>
<widget class="QLabel" name="labelText">
<property name="text">
<string>Message text</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="buttonLayout">
<property name="spacing">
<number>10</number>
</property>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -1,74 +0,0 @@
/*
* 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 "SelectImage.h"
#include "ImageItem.h"
#include "NewsShared/ResourceManagement/Resource.h"
#include "ResourceManagement/BuilderResourceManifest.h"
#include "Qt/ui_SelectImage.h"
namespace News
{
const int SelectImage::MAX_COLS = 2;
SelectImage::SelectImage(const ResourceManifest& manifest)
: QDialog()
, m_ui(new Ui::SelectImageDialog)
, m_manifest(manifest)
{
m_ui->setupUi(this);
auto layout = static_cast<QGridLayout*>(m_ui->scrollAreaContents->layout());
layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
int row = 0;
int col = 0;
//! read all image resources and populate the container
for (auto pResource : m_manifest)
{
if (pResource->GetType().compare("image") == 0)
{
auto imageItem = new ImageItem(*pResource);
connect(imageItem, &ImageItem::selectSignal, this, &SelectImage::ImageSelected);
layout->addWidget(imageItem, row, col, Qt::AlignLeft);
m_images.append(imageItem);
col++;
if (col >= MAX_COLS)
{
col = 0;
row++;
}
}
}
connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
}
SelectImage::~SelectImage() {}
Resource* SelectImage::GetSelected() const
{
return m_pSelected;
}
void SelectImage::ImageSelected(ImageItem* imageItem)
{
for (auto image : m_images)
{
image->SetSelect(image == imageItem);
}
if (imageItem)
{
m_pSelected = &imageItem->GetResource();
}
}
#include "Qt/moc_SelectImage.cpp"
}
@@ -1,46 +0,0 @@
/*
* 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 <QDialog>
#endif
namespace Ui
{
class SelectImageDialog;
}
namespace News {
class ImageItem;
class Resource;
class ResourceManifest;
//! Allows to select existing images for multiple messages without re-uploading same one
class SelectImage
: public QDialog
{
Q_OBJECT
public:
explicit SelectImage(const ResourceManifest& manifest);
~SelectImage();
void Select();
void Close();
Resource* GetSelected() const;
private:
const static int MAX_COLS;
QScopedPointer<Ui::SelectImageDialog> m_ui;
const ResourceManifest& m_manifest;
QList<ImageItem*> m_images;
Resource* m_pSelected = nullptr;
void ImageSelected(ImageItem* imageItem);
};
} // namespace News
@@ -1,106 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SelectImageDialog</class>
<widget class="QDialog" name="SelectImageDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>439</width>
<height>406</height>
</rect>
</property>
<property name="windowTitle">
<string>Select Image</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<widget class="QWidget" name="scrollAreaContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>437</width>
<height>379</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>2</number>
</property>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -1,497 +0,0 @@
/*
* 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 "BuilderResourceManifest.h"
#include "NewsShared/ResourceManagement/QtDownloadManager.h"
#include "NewsBuilder/S3Connector.h"
#include "UidGenerator.h"
#include "NewsShared/ResourceManagement/Resource.h"
#include "NewsBuilder/ResourceManagement/UploadDescriptor.h"
#include "NewsBuilder/ResourceManagement/DeleteDescriptor.h"
#include "NewsShared/ResourceManagement/ArticleDescriptor.h"
#include "NewsBuilder/ResourceManagement/ImageDescriptor.h"
#include "EndpointManager.h"
#include <QJsonArray>
#include <QByteArray>
#include <aws/core/utils/memory/stl/AwsStringStream.h>
#include <QMessageBox>
namespace News {
BuilderResourceManifest::BuilderResourceManifest(
std::function<void()> syncSuccessCallback,
std::function<void(ErrorCode)> syncFailCallback,
std::function<void(QString, LogType)> syncUpdateCallback)
: ResourceManifest(syncSuccessCallback, syncFailCallback, syncUpdateCallback)
, m_s3Connector(new S3Connector)
, m_uidGenerator(new UidGenerator)
, m_endpointManager(new EndpointManager) {}
Resource* BuilderResourceManifest::AddArticle()
{
auto pResource = new Resource(
QString("%1").arg(m_uidGenerator->GenerateUid()),
"article");
QJsonObject json;
json["title"] = "New Article";
json["body"] = "Enter article body here";
json["imageId"] = "0";
QJsonDocument doc(json);
QByteArray data = doc.toJson(QJsonDocument::Compact).toStdString().data();
pResource->SetData(data);
m_toUpload.push(pResource);
AppendResource(pResource);
m_order.append(pResource->GetId());
return pResource;
}
Resource* BuilderResourceManifest::AddImage(const QString& filename)
{
auto pResource = new Resource(
QString("%1").arg(m_uidGenerator->GenerateUid()),
"image");
ImageDescriptor descriptor(*pResource);
QString error;
if (!descriptor.Read(filename, error))
{
m_syncUpdateCallback(error, LogError);
delete pResource;
return nullptr;
}
m_toUpload.push(pResource);
AppendResource(pResource);
return pResource;
}
void BuilderResourceManifest::UpdateResource(Resource* pResource)
{
if (!m_toUpload.contains(pResource))
{
pResource->SetVersion(pResource->GetVersion() + 1);
m_toUpload.push(pResource);
}
}
void BuilderResourceManifest::UseResource(const QString& id)
{
auto pResource = FindById(id, m_resources);
if (!pResource)
{
return;
}
pResource->SetRefCount(pResource->GetRefCount() + 1);
UpdateResource(pResource);
}
void BuilderResourceManifest::FreeResource(const QString& id)
{
auto pResource = FindById(id, m_resources);
if (!pResource)
{
return;
}
if (m_toDelete.contains(pResource))
{
return;
}
pResource->SetRefCount(pResource->GetRefCount() - 1);
if (pResource->GetRefCount() <= 0)
{
m_toDelete.push(pResource);
RemoveResource(pResource);
m_toUpload.removeAll(pResource);
if (pResource->GetType().compare("article") == 0)
{
m_order.removeAll(pResource->GetId());
}
}
else
{
m_toUpload.push(pResource);
}
}
bool BuilderResourceManifest::UpdateArticleOrder(const QString& id, bool direction, ErrorCode& error)
{
int index = m_order.indexOf(id);
if (index == -1)
{
m_syncUpdateCallback(QString("Couldn't find article: %1").arg(id), LogError);
error = ErrorCode::MissingArticle;
return false;
}
m_order.removeAll(id);
index = index + (direction ? -1 : 1);
if (index < 0)
{
index = 0;
}
if (index > m_order.count())
{
index = m_order.count();
}
m_order.insert(index, id);
return true;
}
EndpointManager* BuilderResourceManifest::GetEndpointManager() const
{
return m_endpointManager;
}
void BuilderResourceManifest::Sync()
{
if (!m_endpointManager->GetSelectedEndpoint())
{
FailSync(ErrorCode::NoEndpoint);
return;
}
if (!InitS3Connector())
{
FailSync(ErrorCode::S3Fail);
return;
}
ResourceManifest::Sync();
}
void BuilderResourceManifest::Reset()
{
if (s_syncing)
{
m_syncUpdateCallback("Sync is already running", LogError);
return;
}
m_toUpload.clear();
for (auto pResource : m_toDelete)
{
delete pResource;
}
m_toDelete.clear();
m_uidGenerator->Clear();
ResourceManifest::Reset();
}
void BuilderResourceManifest::PersistLocalResources()
{
// we are switching to another manifest here, while having the local data still present
// so we need to explicitly mark it for uploading so that it does not get deleted
for (auto pResource : m_resources)
{
if (!m_toUpload.contains(pResource))
{
m_toUpload.push(pResource);
}
}
}
void BuilderResourceManifest::SetSyncType(SyncType syncType)
{
m_syncType = syncType;
}
bool BuilderResourceManifest::HasChanges() const
{
return m_toUpload.count() > 0 || m_toDelete.count() > 0;
}
void BuilderResourceManifest::AppendResource(Resource* pResource)
{
m_uidGenerator->AddUid(pResource->GetId().toInt());
ResourceManifest::AppendResource(pResource);
}
void BuilderResourceManifest::RemoveResource(Resource* pResource)
{
m_uidGenerator->RemoveUid(pResource->GetId().toInt());
ResourceManifest::RemoveResource(pResource);
}
void BuilderResourceManifest::OnDownloadFail()
{
QMessageBox msgBox(QMessageBox::Critical,
"Sync failed",
QString("%1\n\n%2")
.arg(GetErrorMessage(ErrorCode::ManifestDownloadFail))
.arg(QObject::tr("Overwrite resource manifest?")),
QMessageBox::Yes | QMessageBox::No);
if (msgBox.exec() == QMessageBox::Yes)
{
if (!UploadManifest())
{
m_failed = true;
m_errorCode = ErrorCode::ManifestUploadFail;
}
}
ResourceManifest::OnDownloadFail();
}
//! This function overrides ResourceManifest Read and tries to do some minimal version checking
//! NOTE: version checking is not done yet, this needs a lot more work to version check properly
ErrorCode BuilderResourceManifest::Read(const QJsonObject& json)
{
int version = json["version"].toInt();
if (version > m_version && m_syncType == SyncType::Verify)
{
return ErrorCode::OutOfSync;
}
m_version = version;
QJsonArray resourceArray = json["resources"].toArray();
// initially mark ALL existing resource for deletion
QList<Resource*> toDelete = m_resources;
for (auto resourceDoc : resourceArray)
{
auto pNewResource = new Resource(resourceDoc.toObject());
// find local resource with the same id as new resource
auto pOldResource = FindById(pNewResource->GetId(), m_resources);
// if resource with the same id already exists then check its version
if (pOldResource)
{
// local resource is outdated, keep its in delete list, and download new one instead
if (pNewResource->GetVersion() > pOldResource->GetVersion())
{
if (m_syncType == SyncType::Merge)
{
m_toDownload.push(pNewResource);
}
else if (m_syncType == SyncType::Overwrite)
{
m_toDelete.push(pNewResource);
}
}
// local resource is newer or same version, keep it (remove from toDelete list)
// and don't need to download new one
else
{
delete pNewResource;
toDelete.removeAll(pOldResource);
}
}
else
{
// if remote resource was NOT deleted locally then download it
if (!FindById(pNewResource->GetId(), m_toDelete))
{
if (m_syncType == SyncType::Merge)
{
m_toDownload.push(pNewResource);
}
else if (m_syncType == SyncType::Overwrite)
{
m_toDelete.push(pNewResource);
}
}
// otherwise user deleted it... needs version check here
else
{
delete pNewResource;
}
}
}
for (auto pResource : m_toUpload)
{
toDelete.removeAll(pResource);
}
for (auto pResource : toDelete)
{
RemoveResource(pResource);
m_toDelete.removeAll(pResource);
delete pResource;
}
//! Sync article display order
//! this is more complex to implement because articles may have been added or deleted
//! remotely by another developer while newsbuilder was running
//! for now just overwrite s3 version
QJsonArray orderArray = json["order"].toArray();
for (auto idObject : orderArray)
{
QString id = idObject.toString();
if (FindById(id, m_toDownload) && !m_order.contains(id))
{
m_order.append(id);
}
}
return ErrorCode::None;
}
//! Write resource manifest to JSON
void BuilderResourceManifest::Write(QJsonObject& json) const
{
QJsonArray resourceArray;
foreach(auto pResource, m_resources)
{
QJsonObject resourceObject;
pResource->Write(resourceObject);
resourceArray.append(resourceObject);
}
json["resources"] = resourceArray;
QJsonArray orderArray;
foreach(auto id, m_order)
{
orderArray.append(id);
}
json["order"] = orderArray;
json["version"] = m_version;
}
//! Figure out how many resources need to be synced
void BuilderResourceManifest::PrepareForSync()
{
// if resource is locally marked for deletion, then we don't need to upload/download it
for (auto pResource : m_toDelete)
{
m_toDownload.removeAll(pResource);
m_toUpload.removeAll(pResource);
}
m_syncLeft =
m_toDownload.size() +
m_toUpload.size() +
m_toDelete.size();
}
void BuilderResourceManifest::SyncResources()
{
ResourceManifest::SyncResources();
UploadResources();
DeleteResources();
}
void BuilderResourceManifest::UploadResources()
{
QStack<Resource*> failures;
while (m_toUpload.count() > 0)
{
m_syncUpdateCallback(
QString("Uploading: %1 resources left").arg(m_toUpload.count()),
LogInfo);
auto pResource = m_toUpload.pop();
auto uploadDescriptor = UploadDescriptor(*pResource);
uploadDescriptor.Upload(*m_s3Connector,
[&](QString url)
{
UpdateSync();
},
[&, pResource](QString error)
{
failures.push(pResource);
m_failed = true;
UpdateSync();
m_syncUpdateCallback(
error,
LogError);
});
}
while (failures.count() > 0)
{
m_toUpload.push(failures.pop());
}
}
void BuilderResourceManifest::DeleteResources()
{
while (m_toDelete.count() > 0)
{
m_syncUpdateCallback(
QString("Deleting: %1 resources left").arg(m_toDelete.count()),
LogInfo);
auto pResource = m_toDelete.pop();
auto deleteDescriptor = DeleteDescriptor(*pResource);
deleteDescriptor.Delete(*m_s3Connector,
[&, pResource]()
{
delete pResource;
UpdateSync();
},
[&, pResource](QString error)
{
m_failed = true;
delete pResource;
UpdateSync();
m_syncUpdateCallback(
QString("Failed to delete resource: %1").arg(error),
LogError);
});
}
}
void BuilderResourceManifest::FinishSync()
{
if (!m_failed)
{
QString error;
if (!UploadManifest())
{
m_failed = true;
m_errorCode = ErrorCode::ManifestUploadFail;
}
}
ResourceManifest::FinishSync();
}
bool BuilderResourceManifest::UploadManifest()
{
m_syncUpdateCallback(QObject::tr("%1 to %2")
.arg("Uploading manifest")
.arg(m_endpointManager->GetSelectedEndpoint()->GetBucket()),
LogInfo);
m_version++;
Aws::String awsError;
QJsonObject manifestObject;
Write(manifestObject);
QJsonDocument doc(manifestObject);
auto ss = Aws::MakeShared<Aws::StringStream>(
S3Connector::ALLOCATION_TAG,
std::ios::in | std::ios::out | std::ios::binary);
*ss << QString(doc.toJson(QJsonDocument::Compact)).toStdString();
Aws::String url;
if (!m_s3Connector->PutObject("resourceManifest", ss, url, awsError))
{
m_syncUpdateCallback(QObject::tr(awsError.c_str()), LogError);
return false;
}
return true;
}
bool BuilderResourceManifest::InitS3Connector() const
{
auto awsProfileName =
m_endpointManager->GetSelectedEndpoint()->GetAwsProfile();
auto bucketName =
m_endpointManager->GetSelectedEndpoint()->GetBucket();
Aws::String awsError;
if (!m_s3Connector->Init(
awsProfileName.toStdString().c_str(),
bucketName.toStdString().c_str(),
awsError))
{
m_syncUpdateCallback(QObject::tr(awsError.c_str()), LogError);
return false;
}
return true;
}
}
@@ -1,125 +0,0 @@
/*
* 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 "NewsShared/ResourceManagement/ResourceManifest.h"
#include <QStack>
class QJsonObject;
namespace News
{
class EndpointManager;
class ArticleDescriptor;
class UidGenerator;
class S3Connector;
class QtDownloadManager;
class Descriptor;
class DownloadDescriptor;
class Resource;
//! Type of sync behavior
enum class SyncType
{
Merge, // merge resources from both endpoints, i.e. replacing outdated and appending missing (also normal sync behavior)
Overwrite, // overwrite resources on new endpoint with old endpoint
Verify // attempt to publish changes but abort if out of sync
};
//! Adds news-builder functionality layer to resource manager
class BuilderResourceManifest
: public ResourceManifest
{
public:
explicit BuilderResourceManifest(
std::function<void()> syncSuccessCallback,
std::function<void(ErrorCode)> syncFailCallback,
std::function<void(QString, LogType)> syncUpdateCallback);
//! Create new article resource
/*!
Create new article with default parameters,
Add it to resource collection,
Mark it for upload.
\retval Resource * - a pointer to an article Resource
*/
Resource* AddArticle();
//! Create new image resource
/*!
Create new image from file,
add it to resource collection,
mark it for upload.
\param filename - path to an image on hard drive
\retval Resource * - a pointer to an image Resource
*/
Resource* AddImage(const QString& filename);
//! If resource was modified, add it to upload list and increment its version
void UpdateResource(Resource* resource);
//! When resource is used by several other resources, this function is called
//! to increment its ref count
void UseResource(const QString& id);
//! When resource is no longer used by another resource
//! decrement refCount,
//! if nothing else is using it, then mark for delete
void FreeResource(const QString& id);
//! Move article either up or down in order queue
bool UpdateArticleOrder(const QString& id, bool direction, ErrorCode& error);
EndpointManager* GetEndpointManager() const;
void Sync() override;
void Reset() override;
//! When switching endpoints Merge allows to persist resources to another endpoint,
//! thus copying news from one location to another upon next Sync
void PersistLocalResources();
void SetSyncType(SyncType syncType);
//! Are there local changes
bool HasChanges() const;
protected:
void AppendResource(Resource* pResource) override;
void RemoveResource(Resource* pResource) override;
void OnDownloadFail() override;
void FinishSync() override;
private:
S3Connector* m_s3Connector = nullptr;
UidGenerator* m_uidGenerator = nullptr;
EndpointManager* m_endpointManager = nullptr;
SyncType m_syncType = SyncType::Merge;
QStack<Resource*> m_toUpload;
QStack<Resource*> m_toDelete;
ErrorCode Read(const QJsonObject& json) override;
void Write(QJsonObject& json) const;
void PrepareForSync() override;
void SyncResources() override;
void UploadResources();
void DeleteResources();
bool UploadManifest();
//! Initializes S3 connector with selected endpoint
bool InitS3Connector() const;
};
} // namespace News
@@ -1,39 +0,0 @@
/*
* 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 "DeleteDescriptor.h"
#include "NewsBuilder/S3Connector.h"
#include "NewsShared/ResourceManagement/Resource.h"
#include <functional>
namespace News
{
DeleteDescriptor::DeleteDescriptor(Resource& resource)
: Descriptor(resource) {}
void DeleteDescriptor::Delete(S3Connector& s3Connector,
std::function<void()> deleteSuccessCallback,
std::function<void(QString)> deleteFailCallback) const
{
Aws::String error;
if (!s3Connector.DeleteObject(
m_resource.GetId().toStdString().c_str(),
error))
{
deleteFailCallback(QString("Error deleting resource %1: %2")
.arg(m_resource.GetId())
.arg(error.c_str()));
}
else
{
deleteSuccessCallback();
}
}
}
@@ -1,29 +0,0 @@
/*
* 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 "NewsShared/ResourceManagement/Descriptor.h"
#include <QString>
#include <functional>
namespace News
{
class S3Connector;
class DeleteDescriptor
: public Descriptor
{
public:
explicit DeleteDescriptor(Resource& resource);
void Delete(S3Connector& s3Connector,
std::function<void()> deleteSuccessCallback,
std::function<void(QString)> deleteFailCallback) const;
};
}
@@ -1,50 +0,0 @@
/*
* 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 "ImageDescriptor.h"
#include "NewsShared/ResourceManagement/Resource.h"
#include <QImageReader>
#include <QBuffer>
#include <QPixmap>
#include <QVariant>
#include <QFile>
namespace News
{
ImageDescriptor::ImageDescriptor(
Resource& resource)
: Descriptor(resource)
{
}
bool ImageDescriptor::Read(const QString& filename, QString& error) const
{
QImageReader reader(filename);
if (!reader.canRead())
{
error = reader.errorString();
return false;
}
QImage image = reader.read();
QPixmap pixmap(filename);
QByteArray data;
QBuffer buffer(&data);
buffer.open(QIODevice::WriteOnly);
QFile test("test.png");
if (pixmap.save(&buffer, "PNG"))
{
m_resource.SetData(data);
return true;
}
error = QString("failed to save image %1").arg(filename);
return false;
}
}
@@ -1,24 +0,0 @@
/*
* 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 <NewsShared/ResourceManagement/Descriptor.h>
class QString;
namespace News
{
class ImageDescriptor
: public Descriptor
{
public:
explicit ImageDescriptor(Resource& resource);
bool Read(const QString& filename, QString& error) const;
};
}
@@ -1,54 +0,0 @@
/*
* 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 "NewsBuilder/S3Connector.h"
#include "UploadDescriptor.h"
#include "NewsShared/ResourceManagement/Resource.h"
#include <functional>
#include <aws/core/utils/memory/stl/AwsStringStream.h>
#include <aws/core/utils/memory/stl/AWSStreamFwd.h>
#include <QObject>
namespace News
{
UploadDescriptor::UploadDescriptor(Resource& resource)
: Descriptor(resource) {}
void UploadDescriptor::Upload(S3Connector& s3Connector,
std::function<void(QString)> uploadSuccessCallback,
std::function<void(QString)> uploadFailCallback) const
{
Aws::String error;
auto ss =
Aws::MakeShared<Aws::StringStream>(S3Connector::ALLOCATION_TAG,
std::ios::in | std::ios::out | std::ios::binary);
auto* pbuf = ss->rdbuf();
pbuf->sputn(m_resource.GetData().data(), m_resource.GetData().size());
Aws::String awsUrl;
if (!s3Connector.PutObject(
m_resource.GetId().toStdString().c_str(),
ss,
m_resource.GetData().size(),
awsUrl,
error))
{
uploadFailCallback(QObject::tr("Error uploading resource %1: %2")
.arg(m_resource.GetId())
.arg(error.c_str()));
}
else
{
uploadSuccessCallback(QString(awsUrl.c_str()));
}
}
}
@@ -1,29 +0,0 @@
/*
* 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 "NewsShared/ResourceManagement/Descriptor.h"
#include <QString>
#include <functional>
namespace News
{
class S3Connector;
class UploadDescriptor
: public Descriptor
{
public:
explicit UploadDescriptor(Resource& resource);
void Upload(S3Connector& s3Connector,
std::function<void(QString)> uploadSuccessCallback,
std::function<void(QString)> uploadFailCallback) const;
};
}
@@ -1,44 +0,0 @@
QLabel
{
font-size: 12px;
color: #FFFFFF;
line-height: 20px;
background-color: transparent;
margin: 0;
}
QLabel#titleLabel
{
font-size: 22px;
line-height: 32px;
}
QLabel#bodyLabel
{
font-size: 14px;
line-height: 20px;
}
QFrame#viewContainer[articleStyle="pinned"]
{
background: rgba(180,139,255,5%);
border: 1px solid #B48BFF;
box-shadow: 0 0 4px 0 rgba(0,0,0,50%);
}
QWidget#articleViewContainerRoot
{
background: #222222;
}
QScrollArea#previewArea,
QWidget#articleViewContents,
QFrame#imageFrame
{
background-color: transparent;
}
News--ArticleView.SelectedArticle > QWidget
{
background: #333333;
}
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4255a25636d77a54809592bc98bef47fa9044be03cd65d73daa821711489e00f
size 7379
-173
View File
@@ -1,173 +0,0 @@
/*
* 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 "S3Connector.h"
#include <fstream>
#include <iostream>
#include <AzFramework/AzFramework_Traits_Platform.h>
AZ_PUSH_DISABLE_WARNING(4251 4819, "-Wunknown-warning-option") // Invalid character not in default code page
#include <aws/core/auth/AWSCredentialsProviderChain.h>
#include <aws/core/utils/Outcome.h>
#include <aws/s3/model/PutObjectRequest.h>
#include <aws/s3/model/GetObjectRequest.h>
#include <aws/s3/model/DeleteObjectRequest.h>
#include <aws/core/utils/memory/stl/AwsStringStream.h>
#include <aws/core/utils/HashingUtils.h>
#include <aws/core/Aws.h>
AZ_POP_DISABLE_WARNING
namespace News
{
const char* S3Connector::ALLOCATION_TAG = "NewsBuilder";
S3Connector::S3Connector()
: m_s3Client(nullptr)
, m_limiter(nullptr)
, m_valid(false)
{
}
S3Connector::~S3Connector() {}
bool S3Connector::Init(const char* awsProfileName, const char* bucket, Aws::String& error)
{
Aws::SDKOptions options;
options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace;
Aws::InitAPI(options);
Aws::Client::ClientConfiguration config;
config.enableTcpKeepAlive = AZ_TRAIT_AZFRAMEWORK_AWS_ENABLE_TCP_KEEP_ALIVE_SUPPORTED;
config.scheme = Aws::Http::Scheme::HTTPS;
config.connectTimeoutMs = 30000;
config.requestTimeoutMs = 30000;
config.readRateLimiter = m_limiter;
config.writeRateLimiter = m_limiter;
auto provider =
Aws::MakeShared<Aws::Auth::ProfileConfigFileAWSCredentialsProvider>(
ALLOCATION_TAG,
awsProfileName);
auto accessKeyId = provider->GetAWSCredentials().GetAWSAccessKeyId();
//! verify that credential file exists
if (accessKeyId.empty())
{
error = "LY_NEWS_DEVELOPER AWS credentials not found. Add credentials in LY Editor AWS->ClientManager";
m_valid = false;
return false;
}
m_bucket = Aws::String(bucket);
m_s3Client = Aws::MakeShared<Aws::S3::S3Client>(
ALLOCATION_TAG,
provider,
config);
m_valid = true;
return true;
}
bool S3Connector::GetObject(const char* key,
Aws::String& data,
Aws::String& url,
Aws::String& error) const
{
if (!m_valid)
{
error = "Client not initialized";
return false;
}
Aws::S3::Model::GetObjectRequest getObjectRequest;
getObjectRequest.SetBucket(m_bucket);
getObjectRequest.SetKey(key);
auto outcome = m_s3Client->GetObject(getObjectRequest);
if (!outcome.IsSuccess())
{
error = outcome.GetError().GetMessage();
return false;
}
url = m_s3Client->GeneratePresignedUrl(m_bucket, key, Aws::Http::HttpMethod::HTTP_GET);
Aws::StringStream ss;
ss << outcome.GetResult().GetBody().rdbuf();
data = ss.str();
return true;
}
bool S3Connector::PutObject(const char* key,
STREAM_PTR stream,
Aws::String& url,
Aws::String& error) const
{
return PutObject(key, stream, GetStreamLength(stream), url, error);
}
bool S3Connector::PutObject(const char* key,
STREAM_PTR stream,
int length,
Aws::String& url,
Aws::String& error) const
{
if (!m_valid)
{
error = "Client not initialized";
return false;
}
Aws::S3::Model::PutObjectRequest putObjectRequest;
putObjectRequest.SetBucket(m_bucket);
putObjectRequest.SetBody(stream);
putObjectRequest.SetContentLength(length);
putObjectRequest.SetContentMD5(
Aws::Utils::HashingUtils::Base64Encode(
Aws::Utils::HashingUtils::CalculateMD5(*putObjectRequest.GetBody())));
putObjectRequest.SetContentType(Aws::String("binary/octet-stream"));
putObjectRequest.SetKey(key);
putObjectRequest.SetACL(Aws::S3::Model::ObjectCannedACL::public_read);
auto outcome = m_s3Client->PutObject(putObjectRequest);
if (!outcome.IsSuccess())
{
error = outcome.GetError().GetMessage();
return false;
}
url = m_s3Client->GeneratePresignedUrl(m_bucket, key, Aws::Http::HttpMethod::HTTP_GET);
return true;
}
bool S3Connector::DeleteObject(const char* key, Aws::String& error) const
{
if (!m_valid)
{
error = "Client not initialized";
return false;
}
Aws::S3::Model::DeleteObjectRequest deleteObjectRequest;
deleteObjectRequest.SetBucket(m_bucket);
deleteObjectRequest.SetKey(key);
auto outcome = m_s3Client->DeleteObject(deleteObjectRequest);
if (!outcome.IsSuccess())
{
error = outcome.GetError().GetMessage();
return false;
}
return true;
}
int S3Connector::GetStreamLength(STREAM_PTR stream)
{
stream->seekg(0, std::ios::end);
auto length = stream->tellg();
stream->seekg(0, std::ios::beg);
return static_cast<int>(length);
}
}
-65
View File
@@ -1,65 +0,0 @@
/*
* 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 <AzCore/PlatformDef.h>
#include <memory>
AZ_PUSH_DISABLE_WARNING(4251 4355 4996, "-Wunknown-warning-option") // <future> includes ppltasks.h which throws a C4355 warning: 'this' used in base member initializer list
#include <aws/s3/S3Client.h>
AZ_POP_DISABLE_WARNING
typedef std::shared_ptr<Aws::IOStream> STREAM_PTR;
namespace News
{
//! A light wrapper around AWS SDK
class S3Connector
{
public:
static const char* ALLOCATION_TAG;
S3Connector();
~S3Connector();
//! Make s3 client using credentials stored in [user]/.aws/credentials file
/*!
\param awsProfileName - name of AWS credentials
\param bucket - name of s3 bucket to use for AWS operations
\param error - error string
\retval bool - true if success else false
*/
bool Init(const char* awsProfileName, const char* bucket, Aws::String& error);
bool GetObject(const char* key,
Aws::String& data,
Aws::String& url,
Aws::String& error) const;
bool PutObject(const char* key,
STREAM_PTR stream,
Aws::String& url,
Aws::String& error) const;
bool PutObject(const char* key,
STREAM_PTR stream,
int length,
Aws::String& url,
Aws::String& error) const;
bool DeleteObject(const char* key,
Aws::String& error) const;
private:
Aws::String m_bucket;
std::shared_ptr<Aws::S3::S3Client> m_s3Client;
std::shared_ptr<Aws::Utils::RateLimits::RateLimiterInterface> m_limiter;
bool m_valid;
static int GetStreamLength(STREAM_PTR stream);
};
} // namespace News
@@ -1,55 +0,0 @@
/*
* 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 "UidGenerator.h"
#include <ctime>
namespace News
{
UidGenerator::UidGenerator()
{
srand(static_cast<int>(time(nullptr)));
}
int UidGenerator::GenerateUid()
{
int uid;
do
{
uid = rand();
}
while (std::find(m_uids.begin(), m_uids.end(), uid) != m_uids.end());
m_uids.push_back(uid);
return uid;
}
int UidGenerator::AddUid(int uid)
{
if (std::find(m_uids.begin(), m_uids.end(), uid) == m_uids.end())
{
m_uids.push_back(uid);
}
return uid;
}
void UidGenerator::RemoveUid(int uid)
{
auto it = std::find(m_uids.begin(), m_uids.end(), uid);
if (it != m_uids.end())
{
m_uids.erase(it);
}
}
void UidGenerator::Clear()
{
m_uids.clear();
}
}
@@ -1,27 +0,0 @@
/*
* 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 <vector>
namespace News
{
//! A simple unique id generator.
class UidGenerator
{
public:
UidGenerator();
int GenerateUid();
int AddUid(int uid);
void RemoveUid(int uid);
void Clear();
private:
std::vector<int> m_uids;
};
}
-35
View File
@@ -1,35 +0,0 @@
/*
* 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 <windows.h>
#include <QApplication>
#include "Qt/NewsBuilder.h"
#include <AzCore/base.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/IO/Path/Path.h>
int main(int argc, char *argv[])
{
// Must be set before QApplication is initialized, so that we support HighDpi monitors, like the Retina displays
// on Windows 10
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
QApplication a(argc, argv);
AZ::IO::FixedMaxPath engineRootPath;
{
AZ::ComponentApplication componentApplication;
auto settingsRegistry = AZ::SettingsRegistry::Get();
settingsRegistry->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
}
News::NewsBuilder w(nullptr, engineRootPath);
w.show();
return a.exec();
}
@@ -1,5 +0,0 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>Resources/NewsBuilder.qss</file>
</qresource>
</RCC>
@@ -1,64 +0,0 @@
#
# 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
#
#
set(FILES
main.cpp
S3Connector.cpp
S3Connector.h
UidGenerator.cpp
UidGenerator.h
EndpointManager.cpp
EndpointManager.h
Qt/ArticleDetails.cpp
Qt/ArticleDetails.h
Qt/ArticleDetails.ui
Qt/ArticleDetailsContainer.cpp
Qt/ArticleDetailsContainer.h
Qt/ArticleDetailsContainer.ui
Qt/AwsDialog.ui
Qt/BuilderArticleViewContainer.cpp
Qt/BuilderArticleViewContainer.h
Qt/BuilderArticleViewContainer.ui
Qt/EndpointEntryView.cpp
Qt/EndpointEntryView.h
Qt/EndpointEntryView.ui
Qt/EndpointManagerView.cpp
Qt/EndpointManagerView.h
Qt/EndpointManagerView.ui
Qt/ImageItem.cpp
Qt/ImageItem.h
Qt/ImageItem.ui
Qt/LogContainer.cpp
Qt/LogContainer.h
Qt/LogContainer.ui
Qt/NewsBuilder.cpp
Qt/NewsBuilder.h
Qt/Newsbuilder.qrc
Qt/Newsbuilder.ui
Qt/QCustomMessageBox.cpp
Qt/QCustomMessageBox.h
Qt/QCustomMessageBox.ui
Qt/SelectImage.cpp
Qt/SelectImage.h
Qt/SelectImage.ui
ResourceManagement/BuilderResourceManifest.cpp
ResourceManagement/BuilderResourceManifest.h
ResourceManagement/DeleteDescriptor.cpp
ResourceManagement/DeleteDescriptor.h
ResourceManagement/ImageDescriptor.cpp
ResourceManagement/ImageDescriptor.h
ResourceManagement/UploadDescriptor.cpp
ResourceManagement/UploadDescriptor.h
)
set(SKIP_UNITY_BUILD_INCLUSION_FILES
# Fix for unity causing the 'News::BuilderResourceManifest::UpdateResourceA' symbol to be unresolved
Qt/ArticleDetails.cpp
# Fix for unity causing the ' Aws::S3::S3Client::GetObjectA' symbol to be unresolved
S3Connector.cpp
)
-51
View File
@@ -1,51 +0,0 @@
/*
* 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 <QString>
namespace News
{
enum class ErrorCode : int
{
None,
OutOfSync,
ManifestDownloadFail,
FailedToSync,
AlreadySyncing,
FailedToParseManifest,
MissingArticle,
NoEndpoint,
ManifestUploadFail,
S3Fail
};
inline extern const char* GetErrorMessage(ErrorCode errorCode)
{
static const char* errors[]
{
"",
"Your manifest is out of sync. Reopen the same endpoint and sync to resolve the conflict and try again.",
"Failed to download resource manifest",
"Failed to sync resources",
"Sync is already running",
"Failed to parse resource manifest",
"Could not find article, try syncing again",
"Missing or incorrect endpoint selected",
"Failed to upload resource manifest",
"Failed to init S3 connection"
};
int errorCount = sizeof errors / sizeof errors[0];
int typeIndex = static_cast<int>(errorCode);
if (typeIndex < 0 || typeIndex >= errorCount)
{
return "Invalid error code";
}
return errors[typeIndex];
}
} // namespace News
-18
View File
@@ -1,18 +0,0 @@
/*
* 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
namespace News {
enum LogType
{
LogOk,
LogInfo,
LogError,
LogWarning
};
} // namespace News
@@ -1,23 +0,0 @@
/*
* 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 "ArticleErrorView.h"
#include "NewsShared/Qt/ui_ArticleErrorView.h"
using namespace News;
ArticleErrorView::ArticleErrorView(
QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::ArticleErrorViewWidget())
{
m_ui->setupUi(this);
}
ArticleErrorView::~ArticleErrorView(){}
#include "NewsShared/Qt/moc_ArticleErrorView.cpp"
@@ -1,37 +0,0 @@
/*
* 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 <QWidget>
#endif
namespace Ui
{
class ArticleErrorViewWidget;
}
namespace AzQtComponents
{
class ExtendedLabel;
}
namespace News
{
class ArticleErrorView
: public QWidget
{
Q_OBJECT
public:
ArticleErrorView(QWidget* parent);
~ArticleErrorView();
private:
QScopedPointer<Ui::ArticleErrorViewWidget> m_ui;
};
}
@@ -1,239 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ArticleErrorViewWidget</class>
<widget class="QWidget" name="ArticleErrorViewWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>192</width>
<height>228</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>ArticleView</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: none; text-align: left;</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="styleSheet">
<string notr="true">border: none;</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>14</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>14</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="AzQtComponents::ExtendedLabel" name="label">
<property name="minimumSize">
<size>
<width>160</width>
<height>90</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="NewsShared.qrc">:/images/Resources/ErrorImage.jpg</pixmap>
</property>
<property name="pixmapSize" stdset="0">
<size>
<width>160</width>
<height>90</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="titleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Open Sans</family>
<pointsize>8</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">a { text-decoration: underline; color: red }</string>
</property>
<property name="text">
<string>We couldnt connect to the network or access our news database. To see the latest Open 3D Engine news, blogs, tutorials, and more, please visit the &lt;a href=&quot;http://aws.amazon.com/lumberyard/&quot;&gt;Lumberyard website&lt;/a&gt;.</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>14</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::ExtendedLabel</class>
<extends>QLabel</extends>
<header>AzQtComponents/Components/ExtendedLabel.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="NewsShared.qrc"/>
</resources>
<connections/>
</ui>
@@ -1,154 +0,0 @@
/*
* 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 "ArticleView.h"
#include <AzQtComponents/Components/ExtendedLabel.h>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: '...' needs to have dll-interface to be used by clients of class '...'
#include "NewsShared/ResourceManagement/ArticleDescriptor.h"
#include "NewsShared/Qt/ui_ArticleView.h"
#include "NewsShared/Qt/ui_PinnedArticleView.h"
#include "NewsShared/ResourceManagement/ResourceManifest.h"
#include "NewsShared/ResourceManagement/Resource.h"
#include <QDesktopServices>
#include <QUrl>
AZ_POP_DISABLE_WARNING
using namespace News;
ArticleView::ArticleView(
QWidget* parent,
const ArticleDescriptor& article,
const ResourceManifest& manifest)
: QWidget(parent)
, m_pArticle(new ArticleDescriptor(article))
, m_manifest(manifest)
, m_icon(nullptr)
{
}
void ArticleView::Update()
{
Q_ASSERT(m_widgetImageFrame && m_widgetTitle && m_widgetBody);
auto pResource = m_manifest.FindById(m_pArticle->GetResource().GetId());
m_pArticle.reset();
if (pResource)
{
m_pArticle = QSharedPointer<ArticleDescriptor>(new ArticleDescriptor(*pResource));
m_widgetTitle->setText(m_pArticle->GetTitle());
m_widgetBody->setText(m_pArticle->GetBody());
auto pImageResource = m_manifest.FindById(m_pArticle->GetImageId());
if (pImageResource)
{
QPixmap pixmap;
if (pixmap.loadFromData(pImageResource->GetData()))
{
if (!m_icon)
{
m_icon = new AzQtComponents::ExtendedLabel(this);
m_icon->setStyleSheet("border: none;");
m_icon->setAlignment(Qt::AlignCenter);
static_cast<QVBoxLayout*>(
m_widgetImageFrame->layout())->insertWidget(0, m_icon);
connect(m_icon, &AzQtComponents::ExtendedLabel::clicked, this, &ArticleView::articleSelectedSlot);
}
m_icon->setPixmap(pixmap.scaled(m_widgetImageFrame->minimumWidth(),
m_widgetImageFrame->minimumHeight(),
Qt::KeepAspectRatioByExpanding));
}
else
{
RemoveIcon();
}
}
else
{
RemoveIcon();
}
}
}
void ArticleView::mousePressEvent([[maybe_unused]] QMouseEvent* event)
{
articleSelectedSlot();
}
void ArticleView::RemoveIcon()
{
if (m_icon)
{
delete m_icon;
m_icon = nullptr;
}
}
void ArticleView::linkActivatedSlot(const QString& link)
{
QDesktopServices::openUrl(QUrl(link));
Q_EMIT linkActivatedSignal(link);
}
void ArticleView::articleSelectedSlot()
{
Q_EMIT articleSelectedSignal(m_pArticle->GetResource().GetId());
}
QSharedPointer<const ArticleDescriptor> ArticleView::GetArticle() const
{
return m_pArticle;
}
void ArticleView::SetupViewWidget(QFrame* widgetImageFrame, AzQtComponents::ExtendedLabel* widgetTitle, AzQtComponents::ExtendedLabel* widgetBody)
{
Q_ASSERT(m_widgetImageFrame == nullptr && m_widgetTitle == nullptr && m_widgetBody == nullptr);
m_widgetImageFrame = widgetImageFrame;
m_widgetTitle = widgetTitle;
m_widgetBody = widgetBody;
connect(m_widgetTitle, &QLabel::linkActivated, this, &ArticleView::linkActivatedSlot);
connect(m_widgetBody, &QLabel::linkActivated, this, &ArticleView::linkActivatedSlot);
connect(m_widgetTitle, &AzQtComponents::ExtendedLabel::clicked, this, &ArticleView::articleSelectedSlot);
connect(m_widgetBody, &AzQtComponents::ExtendedLabel::clicked, this, &ArticleView::articleSelectedSlot);
Q_ASSERT(m_widgetImageFrame && m_widgetTitle && m_widgetBody);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// ArticleViewDefaultWidget
////////////////////////////////////////////////////////////////////////////////////////////////////
ArticleViewDefaultWidget::ArticleViewDefaultWidget(QWidget* parent,
const ArticleDescriptor& article,
const ResourceManifest& manifest)
: ArticleView(parent, article, manifest)
, m_ui(new Ui::ArticleViewWidget())
{
m_ui->setupUi(this);
SetupViewWidget(m_ui->imageFrame, m_ui->titleLabel, m_ui->bodyLabel);
Update();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// ArticleViewPinnedWidget
////////////////////////////////////////////////////////////////////////////////////////////////////
ArticleViewPinnedWidget::ArticleViewPinnedWidget(QWidget* parent,
const ArticleDescriptor& article,
const ResourceManifest& manifest)
: ArticleView(parent, article, manifest)
, m_ui(new Ui::PinnedArticleViewWidget())
{
m_ui->setupUi(this);
SetupViewWidget(m_ui->imageFrame, m_ui->titleLabel, m_ui->bodyLabel);
Update();
}
#include "NewsShared/Qt/moc_ArticleView.cpp"
@@ -1,93 +0,0 @@
/*
* 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 <QWidget>
#endif
namespace Ui
{
class ArticleViewWidget;
class PinnedArticleViewWidget;
}
namespace AzQtComponents
{
class ExtendedLabel;
}
class QFrame;
namespace News
{
class ArticleDescriptor;
class ResourceManifest;
class ArticleView
: public QWidget
{
Q_OBJECT
public:
ArticleView(QWidget* parent,
const ArticleDescriptor& article,
const ResourceManifest& manifest);
~ArticleView() = default;
void Update();
QSharedPointer<const ArticleDescriptor> GetArticle() const;
Q_SIGNALS:
void articleSelectedSignal(QString resourceId);
void linkActivatedSignal(const QString& link);
protected:
void SetupViewWidget(QFrame* widgetImageFrame, AzQtComponents::ExtendedLabel* widgetTitle, AzQtComponents::ExtendedLabel* widgetBody);
void mousePressEvent(QMouseEvent* event);
private:
QFrame* m_widgetImageFrame = nullptr;
AzQtComponents::ExtendedLabel* m_widgetTitle = nullptr;
AzQtComponents::ExtendedLabel* m_widgetBody = nullptr;
AzQtComponents::ExtendedLabel* m_icon = nullptr;
QSharedPointer<const ArticleDescriptor> m_pArticle;
const ResourceManifest& m_manifest;
void RemoveIcon();
private Q_SLOTS:
void linkActivatedSlot(const QString& link);
void articleSelectedSlot();
};
class ArticleViewDefaultWidget : public ArticleView
{
public:
ArticleViewDefaultWidget(QWidget* parent,
const ArticleDescriptor& article,
const ResourceManifest& manifest);
~ArticleViewDefaultWidget() = default;
private:
Ui::ArticleViewWidget* m_ui = nullptr;
};
class ArticleViewPinnedWidget : public ArticleView
{
public:
ArticleViewPinnedWidget(QWidget* parent,
const ArticleDescriptor& article,
const ResourceManifest& manifest);
~ArticleViewPinnedWidget() = default;
private:
Ui::PinnedArticleViewWidget* m_ui = nullptr;
};
}
@@ -1,244 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ArticleViewWidget</class>
<widget class="QWidget" name="ArticleViewWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>430</width>
<height>376</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>430</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>430</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>ArticleView</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="viewContainer">
<property name="articleStyle" stdset="0">
<string>default</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="imageFrame">
<property name="minimumSize">
<size>
<width>430</width>
<height>184</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>430</width>
<height>184</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="AzQtComponents::ExtendedLabel" name="titleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Open Sans</family>
<pointsize>14</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Title:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>false</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>12</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="AzQtComponents::ExtendedLabel" name="bodyLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>Open Sans</family>
<pointsize>8</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Body</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="margin">
<number>0</number>
</property>
<property name="indent">
<number>0</number>
</property>
<property name="openExternalLinks">
<bool>false</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>24</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::ExtendedLabel</class>
<extends>QLabel</extends>
<header>AzQtComponents/Components/ExtendedLabel.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

Some files were not shown because too many files have changed in this diff Show More