Merge pull request #2007 from aws-lumberyard-dev/daimini/NewsBuilder/removal

Remove NewsBuilder project from Editor solution.
This commit is contained in:
Chris Galvan
2021-07-09 13:42:26 -05:00
committed by GitHub
99 changed files with 0 additions and 8697 deletions
-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
@@ -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();
};
-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>
@@ -1,266 +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 "ArticleViewContainer.h"
#include "ArticleView.h"
#include "NewsShared/ResourceManagement/ResourceManifest.h"
#include "NewsShared/ResourceManagement/ArticleDescriptor.h"
#include "NewsShared/ResourceManagement/Resource.h"
#include "NewsShared/Qt/ArticleErrorView.h"
#include "NewsShared/Qt/KeepInTouchView.h"
#include "NewsShared/Qt/ui_ArticleViewContainer.h"
#include <QLabel>
#include <QMap>
namespace News
{
ArticleViewContainer::ArticleViewContainer(QWidget* parent, ResourceManifest& manifest)
: QWidget(parent)
, m_ui(new Ui::ArticleViewContainerWidget)
, m_manifest(manifest)
, m_loadingLabel(nullptr)
, m_errorMessage(nullptr)
{
m_ui->setupUi(this);
AddLoadingMessage();
m_keepInTouchViewWidget = new KeepInTouchView(this);
m_keepInTouchViewWidget->setVisible(false);
connect(m_keepInTouchViewWidget, &KeepInTouchView::linkActivatedSignal,
this, &ArticleViewContainer::linkActivatedSignal);
}
ArticleViewContainer::~ArticleViewContainer() {}
void ArticleViewContainer::PopulateArticles()
{
Clear();
bool articlesFound = false;
for (auto id : m_manifest.GetOrder())
{
auto pResource = m_manifest.FindById(id);
if (pResource && pResource->GetType().compare("article") == 0)
{
AddArticleView(ArticleDescriptor(*pResource));
articlesFound = true;
}
}
if (!articlesFound)
{
AddErrorMessage();
}
else
{
auto layout = static_cast<QVBoxLayout*>(m_ui->articleViewContents->layout());
layout->insertWidget(layout->count() - 1, m_keepInTouchViewWidget);
m_keepInTouchViewWidget->setVisible(true);
}
qApp->processEvents();
}
ArticleView* ArticleViewContainer::FindById(const QString& id)
{
auto it = std::find_if(
m_articles.begin(),
m_articles.end(),
[id](ArticleView* articleView) -> bool
{
if (!articleView)
{
return false;
}
return articleView->GetArticle()->GetResource().GetId().compare(id) == 0;
});
if (it == m_articles.end())
{
return nullptr;
}
return *it;
}
void ArticleViewContainer::AddArticleView(const ArticleDescriptor& articleDesc, int articlePosition)
{
ClearError();
ArticleView* view = CreateArticleView(articleDesc);
if (view == nullptr)
{
return;
}
m_articles.append(view);
connect(view, &ArticleView::articleSelectedSignal,
this, &ArticleViewContainer::articleSelectedSlot);
connect(view, &ArticleView::linkActivatedSignal,
this, &ArticleViewContainer::linkActivatedSignal);
auto layout = static_cast<QVBoxLayout*>(m_ui->articleViewContents->layout());
if (articlePosition == -1)
{
articlePosition = layout->count() - 1;
}
layout->insertWidget(articlePosition, view);
qApp->processEvents();
}
void ArticleViewContainer::DeleteArticleView(ArticleView* view)
{
m_articles.removeAll(view);
m_ui->articleViewContents->layout()->removeWidget(view);
delete view;
}
void ArticleViewContainer::ForceRefreshArticleView(ArticleView* articleView)
{
if (articleView == nullptr)
{
return;
}
QSharedPointer<const ArticleDescriptor> articleDesc = articleView->GetArticle();
auto layout = static_cast<QVBoxLayout*>(m_ui->articleViewContents->layout());
const int viewIndex = layout->indexOf(articleView);
DeleteArticleView(articleView);
AddArticleView(*articleDesc, viewIndex);
}
void ArticleViewContainer::ScrollToView(ArticleView* view) const
{
m_ui->previewArea->ensureWidgetVisible(view);
}
void ArticleViewContainer::ClearError()
{
//delete loading label
if (m_loadingLabel)
{
delete m_loadingLabel;
m_loadingLabel = nullptr;
}
//delete error message
if (m_errorMessage)
{
delete m_errorMessage;
m_errorMessage = nullptr;
}
}
void ArticleViewContainer::articleSelectedSlot(QString id)
{
emit articleSelectedSignal(id);
}
void ArticleViewContainer::UpdateArticleOrder(ArticleView* view, bool direction) const
{
QVBoxLayout* layout = qobject_cast<QVBoxLayout*>(m_ui->articleViewContents->layout());
const int index = layout->indexOf(view);
if (direction && index == 0)
{
return;
}
if (!direction && index == layout->count() - 2)
{
return;
}
const int newIndex = direction ? index - 1 : index + 1;
layout->removeWidget(view);
layout->insertWidget(newIndex, view);
}
void ArticleViewContainer::AddLoadingMessage()
{
if (m_errorMessage)
{
delete m_errorMessage;
m_errorMessage = nullptr;
}
if (!m_loadingLabel)
{
m_loadingLabel = new QLabel(this);
m_loadingLabel->setText("Retrieving news...");
auto layout = static_cast<QVBoxLayout*>(m_ui->articleViewContents->layout());
layout->insertWidget(0, m_loadingLabel);
}
}
void ArticleViewContainer::AddErrorMessage()
{
if (m_loadingLabel)
{
delete m_loadingLabel;
m_loadingLabel = nullptr;
}
if (!m_errorMessage)
{
m_errorMessage = new ArticleErrorView(this);
auto layout = static_cast<QVBoxLayout*>(m_ui->articleViewContents->layout());
layout->insertWidget(0, m_errorMessage);
}
}
void ArticleViewContainer::Clear()
{
ClearError();
for (auto articleView : m_articles)
{
delete articleView;
}
m_articles.clear();
}
ArticleViewContainer::ArticleStyle ArticleViewContainer::GetArticleStyleEnumFromString(const QString& articleStyleStr) const
{
static const QMap<QString, ArticleStyle> articleStyleStringEnumMap = {
{ "default", ArticleStyle::Default },
{ "pinned", ArticleStyle::Pinned }
};
if (articleStyleStringEnumMap.contains(articleStyleStr) == false)
{
Q_ASSERT(false);
return ArticleStyle::Default;
}
return articleStyleStringEnumMap.value(articleStyleStr);
}
ArticleView* ArticleViewContainer::CreateArticleView(const ArticleDescriptor& articleDesc)
{
ArticleStyle articleStyle = GetArticleStyleEnumFromString(articleDesc.GetArticleStyle());
switch(articleStyle)
{
case ArticleStyle::Default:
return new ArticleViewDefaultWidget(this, articleDesc, m_manifest);
case ArticleStyle::Pinned:
return new ArticleViewPinnedWidget(this, articleDesc, m_manifest);
default:
Q_ASSERT(false);
return nullptr;
}
}
}
#include "NewsShared/Qt/moc_ArticleViewContainer.cpp"
@@ -1,81 +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/LogType.h"
#include <QWidget>
#include <QList>
#endif
class QLabel;
namespace Ui
{
class ArticleViewContainerWidget;
}
namespace News
{
class Resource;
class ArticleView;
class ArticleDescriptor;
class ResourceManifest;
class ArticleErrorView;
class KeepInTouchView;
class ArticleViewContainer
: public QWidget
{
Q_OBJECT
enum ArticleStyle
{
Default,
Pinned
};
public:
explicit ArticleViewContainer(QWidget* parent, ResourceManifest& manifest);
~ArticleViewContainer();
virtual void PopulateArticles();
ArticleView* FindById(const QString& id);
void AddArticleView(const ArticleDescriptor& articleDesc, int articlePosition = -1);
void DeleteArticleView(ArticleView* view);
void ForceRefreshArticleView(ArticleView* articleView);
void ScrollToView(ArticleView* view) const;
void UpdateArticleOrder(ArticleView* view, bool direction) const;
void AddLoadingMessage();
void AddErrorMessage();
void Clear();
Q_SIGNALS:
void articleSelectedSignal(QString resourceId);
void addArticle(Resource* article);
void logSignal(QString text, LogType logType = LogInfo);
void scrolled();
void linkActivatedSignal(const QString& link);
private:
QScopedPointer<Ui::ArticleViewContainerWidget> m_ui;
QList<ArticleView*> m_articles;
ResourceManifest& m_manifest;
QLabel* m_loadingLabel;
ArticleErrorView* m_errorMessage;
KeepInTouchView* m_keepInTouchViewWidget = nullptr;
void ClearError();
ArticleStyle GetArticleStyleEnumFromString(const QString& articleStyleStr) const;
ArticleView* CreateArticleView(const ArticleDescriptor& articleDesc);
private Q_SLOTS:
virtual void articleSelectedSlot(QString id);
};
}
@@ -1,104 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ArticleViewContainerWidget</class>
<widget class="QWidget" name="ArticleViewContainerWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>606</width>
<height>753</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</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="QScrollArea" name="previewArea">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
<widget class="QWidget" name="articleViewContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>589</width>
<height>753</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>16</number>
</property>
<property name="leftMargin">
<number>15</number>
</property>
<property name="topMargin">
<number>15</number>
</property>
<property name="rightMargin">
<number>15</number>
</property>
<property name="bottomMargin">
<number>15</number>
</property>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<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 "KeepInTouchView.h"
#include "NewsShared/Qt/ui_KeepInTouchView.h"
#include <QMap>
#include <QUrl>
#include <QString>
#include <QDesktopServices>
namespace News
{
KeepInTouchView::KeepInTouchView(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::KeepInTouchViewWidget())
{
m_ui->setupUi(this);
m_ui->twich_container->setCursor(Qt::PointingHandCursor);
m_ui->twitter_container->setCursor(Qt::PointingHandCursor);
m_ui->youtube_container->setCursor(Qt::PointingHandCursor);
m_ui->facebook_container->setCursor(Qt::PointingHandCursor);
m_ui->twich_container->installEventFilter(this);
m_ui->twitter_container->installEventFilter(this);
m_ui->youtube_container->installEventFilter(this);
m_ui->facebook_container->installEventFilter(this);
}
bool KeepInTouchView::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::MouseButtonRelease)
{
if (watched == m_ui->twich_container)
{
return LaunchSocialMediaUrl(SocialMediaType::Twitch);
}
else if (watched == m_ui->twitter_container)
{
return LaunchSocialMediaUrl(SocialMediaType::Twitter);
}
else if (watched == m_ui->youtube_container)
{
return LaunchSocialMediaUrl(SocialMediaType::YouTube);
}
else if (watched == m_ui->facebook_container)
{
return LaunchSocialMediaUrl(SocialMediaType::Facebook);
}
}
return QWidget::eventFilter(watched, event);
}
bool KeepInTouchView::LaunchSocialMediaUrl(SocialMediaType type)
{
static const QMap<SocialMediaType, const char*> socialMediaTypeToUrlMap =
{
{ SocialMediaType::Twitch, m_twitchUrl },
{ SocialMediaType::Twitter, m_twitterUrl },
{ SocialMediaType::YouTube, m_youtubeUrl },
{ SocialMediaType::Facebook, m_facebookUrl }
};
if (socialMediaTypeToUrlMap.contains(type) == false)
{
return false;
}
QString link = socialMediaTypeToUrlMap[type];
Q_EMIT linkActivatedSignal(link);
return QDesktopServices::openUrl(QUrl(link));
}
}
#include "NewsShared/Qt/moc_KeepInTouchView.cpp"
@@ -1,53 +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 KeepInTouchViewWidget;
}
namespace News
{
class KeepInTouchView
: public QWidget
{
Q_OBJECT
enum class SocialMediaType
{
Twitch,
Twitter,
YouTube,
Facebook
};
public:
KeepInTouchView(QWidget* parent);
~KeepInTouchView() = default;
bool eventFilter(QObject *watched, QEvent *event);
Q_SIGNALS:
void linkActivatedSignal(const QString& link);
private:
bool LaunchSocialMediaUrl(SocialMediaType type);
Ui::KeepInTouchViewWidget* m_ui = nullptr;
const char* m_twitchUrl = "https://docs.aws.amazon.com/console/lumberyard/twitch";
const char* m_twitterUrl = "https://docs.aws.amazon.com/console/lumberyard/twitter";
const char* m_youtubeUrl = "https://docs.aws.amazon.com/console/lumberyard/youtube";
const char* m_facebookUrl = "https://docs.aws.amazon.com/console/lumberyard/facebook";
};
}
@@ -1,423 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KeepInTouchViewWidget</class>
<widget class="QWidget" name="KeepInTouchViewWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>430</width>
<height>336</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 alignment="Qt::AlignTop">
<widget class="QFrame" name="viewContainer">
<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="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<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="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="NewsShared.qrc">:/images/Resources/KeepInTouchBanner.jpg</pixmap>
</property>
</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="Fixed">
<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>Keep in touch!</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="Fixed">
<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>Want to know the latest news at AmazonGameDev? Sign up for our newsletter and use the following links to find us!</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>20</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QWidget" name="socialMediaContainer" native="true">
<property name="styleSheet">
<string notr="true">background: transparent;</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>20</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="twich_container" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>8</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="twitch_icon">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="NewsShared.qrc">:/images/Resources/icon_twitch.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="twitch_label">
<property name="text">
<string>Twitch</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="twitter_container" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>8</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="twitter_icon">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="NewsShared.qrc">:/images/Resources/icon_twitter.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="twitter_label">
<property name="text">
<string>Twitter</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item alignment="Qt::AlignVCenter">
<widget class="QWidget" name="youtube_container" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>8</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="youtube_icon">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="NewsShared.qrc">:/images/Resources/icon_youtube.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="youtube_label">
<property name="text">
<string>Youtube</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item alignment="Qt::AlignLeft">
<widget class="QWidget" name="facebook_container" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="spacing">
<number>8</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="facebook_icon">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="NewsShared.qrc">:/images/Resources/icon_facebook.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="facebook_label">
<property name="text">
<string>Facebook</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</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,10 +0,0 @@
<RCC>
<qresource prefix="images">
<file>../Resources/ErrorImage.jpg</file>
<file>../Resources/KeepInTouchBanner.jpg</file>
<file>../Resources/icon_facebook.png</file>
<file>../Resources/icon_twitch.png</file>
<file>../Resources/icon_twitter.png</file>
<file>../Resources/icon_youtube.png</file>
</qresource>
</RCC>
@@ -1,260 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PinnedArticleViewWidget</class>
<widget class="QWidget" name="PinnedArticleViewWidget">
<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>pinned</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>12</number>
</property>
<property name="leftMargin">
<number>16</number>
</property>
<property name="topMargin">
<number>16</number>
</property>
<property name="rightMargin">
<number>16</number>
</property>
<property name="bottomMargin">
<number>16</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QFrame" name="frame_2">
<property name="minimumSize">
<size>
<width>128</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>128</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: transparent;</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="QFrame" name="imageFrame">
<property name="minimumSize">
<size>
<width>128</width>
<height>96</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>128</width>
<height>96</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">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item alignment="Qt::AlignTop">
<widget class="QFrame" name="frame">
<property name="styleSheet">
<string notr="true">background-color: transparent;</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="AzQtComponents::ExtendedLabel" name="titleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<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="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>false</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse</set>
</property>
<property name="fontStyle" stdset="0">
<string>sectionTitle</string>
</property>
</widget>
</item>
<item>
<widget class="AzQtComponents::ExtendedLabel" name="bodyLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<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="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>false</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</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>
@@ -1,81 +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 "ArticleDescriptor.h"
#include "Resource.h"
#include <QJsonArray>
#include <QJsonDocument>
using namespace News;
ArticleDescriptor::ArticleDescriptor(
Resource& resource)
: JsonDescriptor(resource)
, m_imageId(m_json["image"].toString())
, m_title(m_json["title"].toString())
, m_body(m_json["body"].toString())
, m_order(m_json["order"].toInt())
{
if (m_json.contains("articleStyle") == true)
{
m_articleStyle = m_json["articleStyle"].toString();
}
}
void ArticleDescriptor::Update() const
{
QJsonObject json;
json["image"] = m_imageId;
json["title"] = m_title;
json["body"] = m_body;
json["order"] = m_order;
json["articleStyle"] = m_articleStyle;
QJsonDocument doc(json);
QByteArray data = doc.toJson(QJsonDocument::Compact).toStdString().data();
m_resource.SetData(data);
}
const QString& ArticleDescriptor::GetArticleStyle() const
{
return m_articleStyle;
}
void ArticleDescriptor::SetArticleStyle(const QString& style)
{
m_articleStyle = style;
}
const QString& ArticleDescriptor::GetImageId() const
{
return m_imageId;
}
void ArticleDescriptor::SetImageId(const QString& imageId)
{
m_imageId = imageId;
}
const QString& ArticleDescriptor::GetTitle() const
{
return m_title;
}
void ArticleDescriptor::SetTitle(const QString& title)
{
m_title = title;
}
const QString& ArticleDescriptor::GetBody() const
{
return m_body;
}
void ArticleDescriptor::SetBody(const QString& body)
{
m_body = body;
}
@@ -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 "JsonDescriptor.h"
#include <QJsonObject>
namespace News
{
class Resource;
//! ArticleDescriptor represents Resource as an article
class ArticleDescriptor
: public JsonDescriptor
{
public:
explicit ArticleDescriptor(Resource& resource);
//! If article was modified, call this to update resource data
void Update() const;
const QString& GetArticleStyle() const;
void SetArticleStyle(const QString& style);
const QString& GetImageId() const;
void SetImageId(const QString& imageId);
const QString& GetTitle() const;
void SetTitle(const QString& title);
const QString& GetBody() const;
void SetBody(const QString& body);
private:
QString m_articleStyle = "default";
QString m_imageId;
QString m_title;
QString m_body;
int m_order;
};
}
@@ -1,15 +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 "Descriptor.h"
using namespace News;
Descriptor::Descriptor(Resource& resource)
: m_resource(resource) {}
Descriptor::~Descriptor() {}
@@ -1,32 +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
{
class Resource;
//! Descriptor is a simple solution to add additional functionality to a Resource
/*!
Some descriptors can only work with certain resource types, like AerticleDescriptor
*/
class Descriptor
{
public:
explicit Descriptor(Resource& resource);
virtual ~Descriptor();
Resource& GetResource() const
{
return m_resource;
}
protected:
Resource& m_resource;
};
}
@@ -1,20 +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 "JsonDescriptor.h"
#include "Resource.h"
#include <QJsonDocument>
using namespace News;
JsonDescriptor::JsonDescriptor(Resource& resource)
: Descriptor(resource)
, m_doc(QJsonDocument::fromJson(m_resource.GetData()))
, m_json(m_doc.object())
{
}
@@ -1,28 +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 "Descriptor.h"
#include <QJsonDocument>
#include <QJsonObject>
namespace News
{
//! JsonDescriptor assumes Resource is a JSON file
class JsonDescriptor
: public Descriptor
{
public:
explicit JsonDescriptor(Resource& resource);
protected:
QJsonDocument m_doc;
QJsonObject m_json;
};
}
@@ -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 "QtDownloadManager.h"
#include "QtDownloader.h"
namespace News
{
QtDownloadManager::QtDownloadManager()
: QObject()
, m_worker(new QtDownloader) // this will start the thread which does downloads
{
// make sure the response handlers are queued connections as the worker runs in a different thread
connect(m_worker, &QtDownloader::failed, this, &QtDownloadManager::failedReply, Qt::QueuedConnection);
connect(m_worker, &QtDownloader::successfullyFinished, this, &QtDownloadManager::successfulReply, Qt::QueuedConnection);
}
QtDownloadManager::~QtDownloadManager()
{
// NOTE: we don't delete the QtDownloader; it deletes itself.
// We just tell it to stop
m_worker->Finish();
}
void QtDownloadManager::Download(const QString& url,
std::function<void(QByteArray)> downloadSuccessCallback,
std::function<void()> downloadFailCallback)
{
int downloadId = m_worker->Download(url);
m_downloads[downloadId] = { downloadSuccessCallback, downloadFailCallback };
}
void QtDownloadManager::Abort()
{
m_worker->Abort();
m_downloads.clear();
}
void QtDownloadManager::successfulReply(int downloadId, QByteArray data)
{
m_downloads[downloadId].downloadSuccessCallback(data);
m_downloads.remove(downloadId);
}
void QtDownloadManager::failedReply(int downloadId)
{
m_downloads[downloadId].downloadFailCallback();
m_downloads.remove(downloadId);
}
} // namespace News
#include "NewsShared/ResourceManagement/moc_QtDownloadManager.cpp"
@@ -1,57 +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 <mutex>
#include <functional>
#include <QObject>
#include <QMap>
#endif
namespace News
{
class QtDownloader;
//! QtDownloadManager handles multiple asynchronous downloads
class QtDownloadManager
: public QObject
{
Q_OBJECT
public:
QtDownloadManager();
~QtDownloadManager();
//! Asynchronously download a file from the input url and return it as QByteArray via the success callback
/*!
\param url - file url to download
\param downloadSuccessCallback - if download is successful pass file's data as QByteArray
\param downloadFailCallback - if download failed, pass error message
*/
void Download(const QString& url,
std::function<void(QByteArray)> downloadSuccessCallback,
std::function<void()> downloadFailCallback);
//! Aborts all currently active downloads. Success/failure callbacks will not be called.
void Abort();
private:
void successfulReply(int downloadId, QByteArray data);
void failedReply(int downloadId);
QtDownloader* m_worker = nullptr;
struct DownloadResponses
{
std::function<void(QByteArray)> downloadSuccessCallback;
std::function<void()> downloadFailCallback;
};
QMap<int, DownloadResponses> m_downloads;
};
}
@@ -1,133 +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 "QtDownloader.h"
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QThread>
namespace News
{
QtDownloader::QtDownloader()
: m_thread(new QThread())
{
// make sure that everything that QObject::connects to us knows we're running in a different thread
moveToThread(m_thread);
// handle clean up of both ourselves and of our thread.
// We manage thread clean up so that it can keep running and be cleaned up later, regardless of what
// the thing that created the QtDownloader does
connect(m_thread, &QThread::finished, m_thread, [this] {
m_thread->deleteLater();
deleteLater();
});
auto abortDownloadsHandler = [this] {
auto replies = m_downloads.keys();
for (QNetworkReply* reply : replies)
{
reply->abort();
}
m_downloads.clear();
};
auto queueDownloadHandler = [this](int downloadId, QString url) {
if (m_networkManager)
{
QNetworkReply* reply = m_networkManager->get(QNetworkRequest(QUrl(url)));
m_downloads.insert(reply, downloadId);
}
};
auto createNetworkManagerHandler = [this] {
m_networkManager = new QNetworkAccessManager;
connect(m_networkManager, &QNetworkAccessManager::finished, this, &QtDownloader::downloadFinished);
};
auto deleteNetworkManagerHandler = [this] {
delete m_networkManager;
m_networkManager = nullptr;
};
auto quitHandler = [this] {
// call quit via this callback, so that it executes in the running thread.
// QThread::quit() is actually blocking and waits until everything finishes, so
// we don't want to call it in the main thread
m_thread->quit();
};
// make sure the response handlers are queued connections as the worker runs in one thread, but these triggers
// will be emitted from the main thread
connect(this, &QtDownloader::triggerAbortAll, this, abortDownloadsHandler, Qt::QueuedConnection);
connect(this, &QtDownloader::triggerDownload, this, queueDownloadHandler, Qt::QueuedConnection);
connect(this, &QtDownloader::triggerQuit, this, quitHandler, Qt::QueuedConnection);
// create/delete the QNetworkAccessManager in our thread, to ensure that any slowdowns caused by
// having to create network connectors / load drivers are done in our non-ui thread.
// make sure that these connections are direct so that network requests can't predate the network engine itself
connect(m_thread, &QThread::started, this, createNetworkManagerHandler, Qt::DirectConnection);
connect(m_thread, &QThread::finished, this, deleteNetworkManagerHandler, Qt::DirectConnection);
m_thread->start();
}
QtDownloader::~QtDownloader()
{
}
int QtDownloader::Download(const QString& url)
{
// create a unique id for this download
int downloadId = m_lastId++;
// trigger a download running in our worker thread
Q_EMIT triggerDownload(downloadId, url);
return downloadId;
}
void QtDownloader::Abort()
{
// trigger an abort in our worker thread
Q_EMIT triggerAbortAll();
}
void QtDownloader::Finish()
{
// trigger a quit in our worker thread
Q_EMIT triggerQuit();
}
void QtDownloader::downloadFinished(QNetworkReply* reply)
{
// Note: this will run in our worker thread
int downloadId = m_downloads[reply];
// emit the signal back to the main thread indicating that we're finished, either
// successfully or unsuccessfully
if (reply->error() == QNetworkReply::NoError)
{
Q_EMIT successfullyFinished(downloadId, reply->readAll());
}
else
{
Q_EMIT failed(downloadId);
}
// clean up the reply; have to do this later, according to the Qt docs
reply->deleteLater();
// make sure to remove our reference to this reply from our list of active downloads
m_downloads.remove(reply);
}
#include "NewsShared/ResourceManagement/moc_QtDownloader.cpp"
}
@@ -1,66 +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 <mutex>
#include <functional>
#include <QObject>
#include <QMap>
#endif
class QNetworkAccessManager;
class QNetworkReply;
class QThread;
namespace News
{
//! QtDownloader is a wrapper around Qt's file download functions
/*!
The QtDownloader spins up another thread and does all downloads in that thread.
The public slot methods (Finish, Download and Abort) can all be called from any thread.
The response signals (successfullyFinished and failed) should be QObject::connect to with
Qt::QueuedConnection, as they will be emitted from the worker thread.
*/
class QtDownloader
: public QObject
{
Q_OBJECT
public:
QtDownloader();
~QtDownloader();
public Q_SLOTS:
void Finish();
int Download(const QString& url);
void Abort();
Q_SIGNALS:
void successfullyFinished(int downloadId, QByteArray data);
void failed(int downloadId);
// ***********************************************
// private - DO NOT CONNECT TO outside of the class!
// (qt signals can't be made private)
void triggerAbortAll();
void triggerDownload(int downloadId, QString url);
void triggerQuit();
// ***********************************************
private:
void downloadFinished(QNetworkReply* reply);
int m_lastId = 0;
QMap<QNetworkReply*, int> m_downloads;
QNetworkAccessManager* m_networkManager = nullptr;
QThread* m_thread;
};
}
@@ -1,97 +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 "Resource.h"
#include <QJsonObject>
using namespace News;
Resource::Resource(const QJsonObject& json)
: Resource(
json["id"].toString(),
QByteArray(),
json["url"].toString(),
json["type"].toString(),
json["refCount"].toInt(),
json["version"].toInt()) {}
Resource::Resource(const QString& id, const QString& type)
: Resource(
id,
QByteArray(),
"",
type,
1,
0) {}
Resource::Resource(const QString& id,
const QByteArray& data,
[[maybe_unused]] const QString& url,
const QString& type,
int refCount,
int version)
: m_id(id)
, m_data(data)
, m_type(type)
, m_refCount(refCount)
, m_version(version) {}
Resource::~Resource() {}
void Resource::Write(QJsonObject& json) const
{
json["id"] = m_id;
json["type"] = m_type;
json["refCount"] = m_refCount;
json["version"] = m_version;
}
QString Resource::GetId() const
{
return m_id;
}
void Resource::SetId(const QString& id)
{
m_id = id;
}
QByteArray Resource::GetData() const
{
return m_data;
}
void Resource::SetData(QByteArray data)
{
m_data = data;
}
QString Resource::GetType() const
{
return m_type;
}
int Resource::GetRefCount() const
{
return m_refCount;
}
void Resource::SetRefCount(int refCount)
{
m_refCount = refCount;
}
int Resource::GetVersion() const
{
return m_version;
}
void Resource::SetVersion(int version)
{
m_version = version;
}
@@ -1,68 +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>
class QJsonObject;
namespace News
{
class Descriptor;
//! Resource is a central element of in-editor messages
//! It represents articles, images, and anything else that is part of news feed
class Resource
{
public:
//! resources are stored as json objects in \ref News::ResourceManifest
//! this creates resource with empty data array, that can be downloaded later
//! by calling News::ResourceManifest::Sync
explicit Resource(const QJsonObject& json);
explicit Resource(const QString& id,
const QString& type);
Resource(const QString& id,
const QByteArray& data,
const QString& url,
const QString& type,
int refCount,
int version);
~Resource();
//! Saves resource's description to a json file
void Write(QJsonObject& json) const;
QString GetId() const;
void SetId(const QString& id);
QByteArray GetData() const;
void SetData(QByteArray data);
QString GetType() const;
int GetRefCount() const;
void SetRefCount(int refCount);
int GetVersion() const;
void SetVersion(int version);
private:
QString m_id;
QByteArray m_data;
QString m_type;
int m_refCount;
int m_version;
};
}
@@ -1,349 +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 "ResourceManifest.h"
#include "NewsShared/ResourceManagement/QtDownloadManager.h"
#include "NewsShared/ResourceManagement/Resource.h"
#include "NewsShared/ResourceManagement/ArticleDescriptor.h"
#include <QJsonArray>
#include <QByteArray>
#include <QFile>
#include <QTextStream>
#include <QCoreApplication>
namespace News
{
const QString ResourceManifest::MANIFEST_NAME = "resourceManifest";
bool ResourceManifest::s_syncing = false;
ResourceManifest::ResourceManifest(
std::function<void()> syncSuccessCallback,
std::function<void(ErrorCode)> syncFailCallback,
std::function<void(QString, LogType)> syncUpdateCallback)
: m_downloader(new QtDownloadManager)
, m_syncSuccessCallback(syncSuccessCallback)
, m_syncFailCallback(syncFailCallback)
, m_syncUpdateCallback(syncUpdateCallback)
{
}
ResourceManifest::~ResourceManifest()
{
// clean everything up
DeleteResources();
delete m_downloader;
}
Resource* ResourceManifest::FindById(const QString& id) const
{
return FindById(id, m_resources);
}
Resource* ResourceManifest::FindById(const QString& id, const QList<Resource*>& resources)
{
auto it = std::find_if(
resources.begin(),
resources.end(),
[id](Resource* resource) -> bool
{
return resource->GetId().compare(id) == 0;
});
if (it == resources.end())
{
return nullptr;
}
return *it;
}
Resource* ResourceManifest::FindById(const QString& id,
const QStack<Resource*>& resources)
{
auto it = std::find_if(
resources.begin(),
resources.end(),
[id](Resource* resource) -> bool
{
return resource->GetId().compare(id) == 0;
});
if (it == resources.end())
{
return nullptr;
}
return *it;
}
void ResourceManifest::Sync()
{
if (s_syncing)
{
FailSync(ErrorCode::AlreadySyncing);
return;
}
s_syncing = true;
m_failed = false;
m_syncUpdateCallback("Starting sync", LogInfo);
ReadConfig();
// first download the manifest json
m_syncUpdateCallback("Downloading manifest", LogInfo);
m_downloader->Download(QString(m_url).append(MANIFEST_NAME),
std::bind(&ResourceManifest::OnDownloadSuccess, this, std::placeholders::_1),
std::bind(&ResourceManifest::OnDownloadFail, this));
}
void ResourceManifest::Abort()
{
m_aborted = true;
m_downloader->Abort();
}
void ResourceManifest::Reset()
{
if (s_syncing)
{
m_syncUpdateCallback("Sync is already running", LogError);
return;
}
m_aborted = false;
m_failed = false;
m_version = -1;
DeleteResources();
m_order.clear();
}
QList<Resource*>::const_iterator ResourceManifest::begin() const
{
return m_resources.constBegin();
}
QList<Resource*>::const_iterator ResourceManifest::end() const
{
return m_resources.constEnd();
}
QList<QString> ResourceManifest::GetOrder() const
{
return m_order;
}
void ResourceManifest::OnDownloadSuccess(QByteArray data)
{
QJsonDocument doc(QJsonDocument::fromJson(data));
if (doc.isNull())
{
FailSync(ErrorCode::FailedToParseManifest);
return;
}
ErrorCode error = Read(doc.object());
if (error != ErrorCode::None)
{
FailSync(error);
return;
}
// check how many resources to sync
PrepareForSync();
// if there is anything to sync, do that
if (m_syncLeft > 0)
{
m_syncUpdateCallback("Syncing resources", LogInfo);
SyncResources();
}
// otherwise just finish sync
else
{
m_syncUpdateCallback("No new resources to sync", LogInfo);
FinishSync();
}
}
void ResourceManifest::OnDownloadFail()
{
FailSync(ErrorCode::ManifestDownloadFail);
}
ErrorCode ResourceManifest::Read(const QJsonObject& json)
{
m_version = json["version"].toInt();
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 it in delete list, and download new one instead
if (pNewResource->GetVersion() > pOldResource->GetVersion())
{
m_toDownload.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);
}
}
// resource with same id not found
else
{
m_toDownload.push(pNewResource);
}
}
// delete everything that's not in s3
for (auto pResource : toDelete)
{
RemoveResource(pResource);
delete pResource;
}
// parse order of articles
m_order.clear();
QJsonArray orderArray = json["order"].toArray();
for (auto idObject : orderArray)
{
m_order.append(idObject.toString());
}
return ErrorCode::None;
}
void ResourceManifest::PrepareForSync()
{
if (m_aborted)
{
m_syncLeft = 0;
}
m_syncLeft = m_toDownload.count();
}
void ResourceManifest::SyncResources()
{
DownloadResources();
}
void ResourceManifest::DownloadResources()
{
while (m_toDownload.count() > 0)
{
m_syncUpdateCallback(
QString("Downloading: %1 resources left").arg(m_toDownload.count()),
LogInfo);
auto pResource = m_toDownload.pop();
m_downloader->Download(QString(m_url).append(pResource->GetId()),
//download success
[&, pResource](QByteArray data)
{
pResource->SetData(data);
AppendResource(pResource);
UpdateSync();
},
//download fail
[&, pResource]()
{
m_failed = true;
delete pResource;
m_syncUpdateCallback("Failed to download resource", LogError);
UpdateSync();
});
}
}
void ResourceManifest::ReadConfig()
{
QFile file(QCoreApplication::applicationDirPath() + "/newsConfig.txt");
if (file.exists())
{
if (file.open(QIODevice::ReadOnly))
{
QTextStream in(&file);
m_url = in.readAll().trimmed();
file.close();
}
}
}
void ResourceManifest::DeleteResources()
{
for (auto pResource : m_toDownload)
{
delete pResource;
}
m_toDownload.clear();
for (auto pResource : m_resources)
{
delete pResource;
}
m_resources.clear();
}
void ResourceManifest::UpdateSync()
{
m_syncLeft--;
if (m_syncLeft == 0)
{
if (!m_failed)
{
FinishSync();
}
else
{
FailSync(ErrorCode::FailedToSync);
}
}
}
void ResourceManifest::FinishSync()
{
if (!m_failed)
{
m_syncSuccessCallback();
}
else
{
m_syncFailCallback(m_errorCode);
}
s_syncing = false;
}
void ResourceManifest::FailSync(ErrorCode error)
{
m_failed = true;
m_errorCode = error;
FinishSync();
}
void ResourceManifest::AppendResource(Resource* pResource)
{
m_resources.append(pResource);
}
void ResourceManifest::RemoveResource(Resource* pResource)
{
m_resources.removeAll(pResource);
}
}
@@ -1,134 +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 <QStack>
#include <functional>
#include "NewsShared/LogType.h"
#include "NewsShared/ErrorCodes.h"
class QJsonObject;
namespace News
{
class ArticleDescriptor;
class UidGenerator;
class S3Connector;
class QtDownloadManager;
class Descriptor;
class DownloadDescriptor;
class Resource;
//! ResourceManifest manages resources.
/*!
Manifest contains information on resources, it handles syncing resources with s3
*/
class ResourceManifest
{
public:
//! ResourceManifest ctor
/*!
\param syncSuccessCallback - called once when everything is synced
\param syncFailCallback - called once when sync failed
\param syncUpdateCallback - called multiple times to update information on sync process
*/
explicit ResourceManifest(
std::function<void()> syncSuccessCallback,
std::function<void(ErrorCode)> syncFailCallback,
std::function<void(QString, LogType)> syncUpdateCallback);
virtual ~ResourceManifest();
//! Find a resource that matches id
/*!
\retval Resource * - a pointer to a Resource with matching id, if none found return nullptr
*/
Resource* FindById(const QString& id) const;
static Resource* FindById(const QString& id, const QList<Resource*>& resources);
static Resource* FindById(const QString& id, const QStack<Resource*>& resources);
//! Sync resources with s3
/*
1) First download resource manifest file
2) Parse manifest
3) Determine which resources need to be downloaded, updated, or deleted
4) Download missing resources or resource that are out of date
5) Call m_syncSuccessCallback
*/
virtual void Sync();
//! Gracegully stop sync process
/*!
Aborting works differently depending at what point during sync porocess it is called
If called before resources started to download, then skip download altogether
Otherwise gracefully abort all downloads and call m_syncFailCallback
*/
void Abort();
//! Called when switching endpoints to reset resource manifest to a clean state
virtual void Reset();
QList<Resource*>::const_iterator begin() const;
QList<Resource*>::const_iterator end() const;
//! Get order of article resources, so they can be displayed properly in ArticleViewContainer
QList<QString> GetOrder() const;
protected:
//! The root location of cloudfront resources
QString m_url = "https://lumberyard-data.amazon.com/";
//! Name of resourceManifest file that links all other resources
static const QString MANIFEST_NAME;
//! Identifies whether syncing is in progress
static bool s_syncing;
//! Manifest Version
int m_version = -1;
//! Number of resources left to sync
int m_syncLeft = 0;
//! Identifies whether sync process was aborted
bool m_aborted = false;
//! Indentifies whether sync process has failed
bool m_failed = false;
ErrorCode m_errorCode = ErrorCode::None;
QtDownloadManager* m_downloader = nullptr;
QList<Resource*> m_resources;
QList<QString> m_order;
QStack<Resource*> m_toDownload;
std::function<void()> m_syncSuccessCallback;
std::function<void(ErrorCode)> m_syncFailCallback;
std::function<void(QString, LogType)> m_syncUpdateCallback;
//! Parse resource manifest json, and figure out which resources need to be downloaded
virtual ErrorCode Read(const QJsonObject& json);
//! Executed before sync to figure out how many resources need to be synced
virtual void PrepareForSync();
//! Actual sync function
virtual void SyncResources();
//! Check whether everything is synced, if so call ResourceManifest::FinishSync
void UpdateSync();
//! Notify that everything is synced
virtual void FinishSync();
void FailSync(ErrorCode error);
virtual void AppendResource(Resource* pResource);
virtual void RemoveResource(Resource* pResource);
virtual void OnDownloadSuccess(QByteArray data);
virtual void OnDownloadFail();
virtual void DownloadResources();
private:
void ReadConfig();
void DeleteResources();
};
} // namespace News
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0a7430d63820b3b00e03b4a8ec73b5e8e3946659031e3aeeb355b632acca4122
size 8857
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:09c1733f116c11b56c54f7c3b6b4462884116db06605ca3cdfa23c45a3695385
size 68024
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f8b5493c81354b6757a21bea6baedd5665b8cb9ca19a6ff4fbd40afef534f35f
size 1257
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2882aad73e2ecf310934ec3eacd0c1fa0a6ff012a4c3c9cf4ca96a72cef2fb8c
size 514
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:046e14f1d90f77bb6ea0fe700bb10049748b46bb84c8ac3620642d4bca9df533
size 954
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3abaf8b178763441ac6812ae518c2f8cbea9b228aa1f84052a65836b33dc980c
size 976
-45
View File
@@ -1,45 +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
NewsShared/LogType.h
NewsShared/ErrorCodes.h
NewsShared/Qt/ArticleErrorView.cpp
NewsShared/Qt/ArticleErrorView.h
NewsShared/Qt/ArticleErrorView.ui
NewsShared/Qt/ArticleView.cpp
NewsShared/Qt/ArticleView.h
NewsShared/Qt/ArticleView.ui
NewsShared/Qt/PinnedArticleView.ui
NewsShared/Qt/ArticleViewContainer.cpp
NewsShared/Qt/ArticleViewContainer.h
NewsShared/Qt/ArticleViewContainer.ui
NewsShared/Qt/KeepInTouchView.cpp
NewsShared/Qt/KeepInTouchView.h
NewsShared/Qt/KeepInTouchView.ui
NewsShared/Qt/NewsShared.qrc
NewsShared/ResourceManagement/ArticleDescriptor.cpp
NewsShared/ResourceManagement/ArticleDescriptor.h
NewsShared/ResourceManagement/Descriptor.cpp
NewsShared/ResourceManagement/Descriptor.h
NewsShared/ResourceManagement/JsonDescriptor.cpp
NewsShared/ResourceManagement/JsonDescriptor.h
NewsShared/ResourceManagement/QtDownloader.cpp
NewsShared/ResourceManagement/QtDownloader.h
NewsShared/ResourceManagement/QtDownloadManager.cpp
NewsShared/ResourceManagement/QtDownloadManager.h
NewsShared/ResourceManagement/Resource.cpp
NewsShared/ResourceManagement/Resource.h
NewsShared/ResourceManagement/ResourceManifest.cpp
NewsShared/ResourceManagement/ResourceManifest.h
NewsShared/Resources/ErrorImage.jpg
NewsShared/Resources/KeepInTouchBanner.jpg
NewsShared/Resources/icon_facebook.png
NewsShared/Resources/icon_twitch.png
NewsShared/Resources/icon_twitter.png
NewsShared/Resources/icon_youtube.png
)