Files
o3de/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Style.cpp
T

488 lines
16 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Component/Entity.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/EntityUtils.h>
#include <algorithm>
AZ_PUSH_DISABLE_WARNING(4251 4800 4244, "-Wunknown-warning-option")
#include <QVariant>
#include <QFont>
#include <QFontInfo>
#include <QPen>
#include <QDebug>
AZ_POP_DISABLE_WARNING
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Styling/definitions.h>
#include <GraphCanvas/Styling/Style.h>
#include <GraphCanvas/Styling/StyleHelper.h>
namespace AZ
{
AZ_TYPE_INFO_SPECIALIZE(QVariant, "{EFF68052-50FF-4072-8047-31243EF95FEE}");
}
namespace
{
static bool StyleVersionConverter(AZ::SerializeContext& /*serializeContext*/, AZ::SerializeContext::DataElementNode& classElement)
{
if (classElement.GetVersion() <= 3)
{
auto selectorArrayElementIndex = classElement.FindElement(AZ_CRC("Selectors", 0x0e4270a0));
if (selectorArrayElementIndex != -1)
{
classElement.RemoveElement(selectorArrayElementIndex);
}
}
return true;
}
namespace Styling = GraphCanvas::Styling;
QString AttributeName(Styling::Attribute attribute)
{
switch (attribute)
{
case Styling::Attribute::BackgroundColor:
return Styling::Attributes::BackgroundColor;
case Styling::Attribute::BackgroundImage:
return Styling::Attributes::BackgroundImage;
case Styling::Attribute::CapStyle:
return Styling::Attributes::CapStyle;
case Styling::Attribute::GridMajorWidth:
return Styling::Attributes::GridMajorWidth;
case Styling::Attribute::GridMajorStyle:
return Styling::Attributes::GridMajorStyle;
case Styling::Attribute::GridMajorColor:
return Styling::Attributes::GridMajorColor;
case Styling::Attribute::GridMinorWidth:
return Styling::Attributes::GridMinorWidth;
case Styling::Attribute::GridMinorStyle:
return Styling::Attributes::GridMinorStyle;
case Styling::Attribute::GridMinorColor:
return Styling::Attributes::GridMinorColor;
case Styling::Attribute::FontFamily:
return Styling::Attributes::FontFamily;
case Styling::Attribute::FontSize:
return Styling::Attributes::FontSize;
case Styling::Attribute::FontWeight:
return Styling::Attributes::FontWeight;
case Styling::Attribute::FontStyle:
return Styling::Attributes::FontStyle;
case Styling::Attribute::FontVariant:
return Styling::Attributes::FontVariant;
case Styling::Attribute::Color:
return Styling::Attributes::Color;
case Styling::Attribute::BorderWidth:
return Styling::Attributes::BorderWidth;
case Styling::Attribute::BorderStyle:
return Styling::Attributes::BorderStyle;
case Styling::Attribute::BorderColor:
return Styling::Attributes::BorderColor;
case Styling::Attribute::BorderRadius:
return Styling::Attributes::BorderRadius;
case Styling::Attribute::LineWidth:
return Styling::Attributes::LineWidth;
case Styling::Attribute::LineStyle:
return Styling::Attributes::LineStyle;
case Styling::Attribute::LineColor:
return Styling::Attributes::LineColor;
case Styling::Attribute::LineCurve:
return Styling::Attributes::LineCurve;
case Styling::Attribute::LineSelectionPadding:
return Styling::Attributes::LineSelectionPadding;
case Styling::Attribute::Margin:
return Styling::Attributes::Margin;
case Styling::Attribute::Padding:
return Styling::Attributes::Padding;
case Styling::Attribute::Width:
return Styling::Attributes::Width;
case Styling::Attribute::Height:
return Styling::Attributes::Height;
case Styling::Attribute::MinWidth:
return Styling::Attributes::MinWidth;
case Styling::Attribute::MaxWidth:
return Styling::Attributes::MaxWidth;
case Styling::Attribute::MinHeight:
return Styling::Attributes::MinHeight;
case Styling::Attribute::MaxHeight:
return Styling::Attributes::MaxHeight;
case Styling::Attribute::Selectors:
return Styling::Attributes::Selectors;
case Styling::Attribute::TextAlignment:
return Styling::Attributes::TextAlignment;
case Styling::Attribute::Invalid:
default:
return "Invalid Attribute";
}
}
class QVariantSerializer
: public AZ::SerializeContext::IDataSerializer
{
/// Store the class data into a binary buffer
virtual size_t Save(const void* classPtr, AZ::IO::GenericStream& stream, bool isDataBigEndian /*= false*/)
{
auto variant = reinterpret_cast<const QVariant*>(classPtr);
QByteArray buffer;
QDataStream qtStream(&buffer, QIODevice::WriteOnly);
qtStream.setByteOrder(isDataBigEndian ? QDataStream::BigEndian : QDataStream::LittleEndian);
qtStream << *variant;
return static_cast<size_t>(stream.Write(buffer.length(), reinterpret_cast<const void*>(buffer.data())));
}
/// Convert binary data to text
virtual size_t DataToText(AZ::IO::GenericStream& in, AZ::IO::GenericStream& out, bool isDataBigEndian /*= false*/)
{
(void)isDataBigEndian;
QByteArray buffer = ReadAll(in);
QByteArray&& base64 = buffer.toBase64();
return static_cast<size_t>(out.Write(base64.size(), base64.data()));
}
/// Convert text data to binary, to support loading old version formats. We must respect text version if the text->binary format has changed!
virtual size_t TextToData(const char* text, unsigned int textVersion, AZ::IO::GenericStream& stream, bool isDataBigEndian = false)
{
(void)textVersion;
(void)isDataBigEndian;
AZ_Assert(textVersion == 0, "Unknown char, short, int version!");
QByteArray decoder = QByteArray::fromBase64(text);
stream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
return static_cast<size_t>(stream.Write(decoder.size(), decoder.constData()));
}
/// Load the class data from a stream.
virtual bool Load(void* classPtr, AZ::IO::GenericStream& in, unsigned int, bool isDataBigEndian = false)
{
QByteArray buffer = ReadAll(in);
QDataStream qtStream(&buffer, QIODevice::ReadOnly);
qtStream.setByteOrder(isDataBigEndian ? QDataStream::BigEndian : QDataStream::LittleEndian);
QVariant* variant = reinterpret_cast<QVariant*>(classPtr);
qtStream >> *variant;
return true;
}
bool CompareValueData(const void* left, const void* right) override
{
auto leftVariant = reinterpret_cast<const QVariant*>(left);
auto rightVariant = reinterpret_cast<const QVariant*>(right);
return *leftVariant == *rightVariant;
}
private:
QByteArray ReadAll(AZ::IO::GenericStream& in)
{
const size_t length = in.GetLength();
QByteArray buffer;
buffer.reserve(static_cast<int>(length));
size_t processed = 0;
size_t read = 0;
char* data = buffer.data();
while (processed < length)
{
read = in.Read(length - processed, data + processed);
if (!read)
{
break;
}
processed += read;
}
AZ_Assert(processed == length, "Incorrect amount of data read from stream");
buffer.resize(static_cast<int>(processed));
return buffer;
}
};
} // namespace
QDataStream& operator<<(QDataStream& out, const Qt::PenStyle& pen)
{
out << static_cast<int>(pen);
return out;
}
QDataStream& operator>>(QDataStream& in, Qt::PenStyle& pen)
{
int holder;
in >> holder;
pen = static_cast<Qt::PenStyle>(holder);
return in;
}
QDataStream& operator<<(QDataStream& out, const Qt::PenCapStyle& cap)
{
out << static_cast<int>(cap);
return out;
}
QDataStream& operator>>(QDataStream& in, Qt::PenCapStyle& cap)
{
int holder;
in >> holder;
cap = static_cast<Qt::PenCapStyle>(holder);
return in;
}
QDataStream& operator<<(QDataStream& out, const Qt::AlignmentFlag& alignment)
{
out << static_cast<int>(alignment);
return out;
}
QDataStream& operator>>(QDataStream& in, Qt::AlignmentFlag& alignment)
{
int holder;
in >> holder;
alignment = static_cast<Qt::AlignmentFlag>(holder);
return in;
}
QDataStream& operator<<(QDataStream& out, const GraphCanvas::Styling::ConnectionCurveType& curve)
{
out << static_cast<int>(curve);
return out;
}
QDataStream& operator>>(QDataStream& in, GraphCanvas::Styling::ConnectionCurveType& curve)
{
int holder;
in >> holder;
curve = static_cast<GraphCanvas::Styling::ConnectionCurveType>(holder);
return in;
}
QDataStream& operator<<(QDataStream& out, const GraphCanvas::Styling::PaletteStyle& curve)
{
out << static_cast<int>(curve);
return out;
}
QDataStream& operator >> (QDataStream& in, GraphCanvas::Styling::PaletteStyle& curve)
{
int holder;
in >> holder;
curve = static_cast<GraphCanvas::Styling::PaletteStyle>(holder);
return in;
}
namespace GraphCanvas
{
namespace Styling
{
//////////
// Style
//////////
void Style::Reflect(AZ::ReflectContext* context)
{
static bool reflected = false;
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (!serializeContext || (reflected && !serializeContext->IsRemovingReflection()))
{
return;
}
qRegisterMetaTypeStreamOperators<Qt::PenStyle>();
qRegisterMetaTypeStreamOperators<Qt::PenCapStyle>();
qRegisterMetaTypeStreamOperators<Qt::AlignmentFlag>();
qRegisterMetaTypeStreamOperators<Styling::ConnectionCurveType>();
qRegisterMetaTypeStreamOperators<Styling::PaletteStyle>();
// Allow QVectors to be serialized
serializeContext->Class<QVariant>()
->Serializer<QVariantSerializer>();
//TODO port collection class away from QHash so it can be serialized
serializeContext->Class<Style>()
->Version(4, &StyleVersionConverter)
->Field("Selectors", &Style::m_selectors)
->Field("SelectorsAsString", &Style::m_selectorsAsString)
->Field("Attributes", &Style::m_values)
;
reflected = true;
}
Style::Style(const SelectorVector& selectors)
: m_selectors(selectors)
, m_selectorsAsString(SelectorsToString(selectors))
{
}
Style::Style(std::initializer_list<Selector> selectors)
: m_selectors(selectors)
, m_selectorsAsString(SelectorsToString(m_selectors))
{
}
Style::~Style()
{
}
int Style::Matches(const AZ::EntityId& object) const
{
for (const Selector& selector : m_selectors)
{
if (selector.Matches(object))
{
return selector.GetComplexity();
}
}
return 0;
}
bool Style::HasAttribute(Attribute attribute) const
{
return m_values.find(attribute) != m_values.end();
}
inline QVariant Style::GetAttribute(Attribute attribute) const
{
return HasAttribute(attribute) ? m_values.at(attribute) : QVariant();
}
void Style::SetAttribute(Attribute attribute, const QVariant& value)
{
m_values[attribute] = value;
}
void Style::Dump() const
{
qDebug() << SelectorsToString(m_selectors).c_str();
for (auto& entry : m_values)
{
qDebug() << AttributeName(entry.first) << entry.second;
}
qDebug() << "";
}
//////////////////
// ComputedStyle
//////////////////
void ComputedStyle::Reflect(AZ::ReflectContext * context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (!serializeContext)
{
return;
}
Style::Reflect(context);
serializeContext->Class<ComputedStyle, AZ::Component>()
->Version(2)
->Field("ObjectSelectors", &ComputedStyle::m_objectSelectors)
->Field("ObjectSelectorsAsString", &ComputedStyle::m_objectSelectorsAsString)
->Field("Styles", &ComputedStyle::m_styles)
;
}
ComputedStyle::ComputedStyle(const EditorId& editorId, const SelectorVector& objectSelectors, StyleVector&& styles)
: m_objectSelectors(objectSelectors)
, m_objectSelectorsAsString(SelectorsToString(m_objectSelectors))
, m_styles(std::move(styles))
{
StyleManagerNotificationBus::Handler::BusConnect(editorId);
}
ComputedStyle::~ComputedStyle()
{
}
void ComputedStyle::Activate()
{
StyleRequestBus::Handler::BusConnect(GetEntityId());
}
void ComputedStyle::Deactivate()
{
StyleRequestBus::Handler::BusDisconnect();
}
AZStd::string ComputedStyle::GetDescription() const
{
AZStd::string result("Computed:\n");
result += "\tObject selectors: " + m_objectSelectorsAsString + "\n";
result += "\tStyles:\n";
for (const Style* style : m_styles)
{
if (style)
{
result += "\t\t" + style->GetSelectorsAsString() + "\n";
}
}
result += "\n";
return result;
}
bool ComputedStyle::HasAttribute(AZ::u32 attribute) const
{
Attribute typedAttribute = static_cast<Attribute>(attribute);
return std::any_of(m_styles.cbegin(), m_styles.cend(), [=](const Style* s) {
return s && s->HasAttribute(typedAttribute);
});
}
QVariant ComputedStyle::GetAttribute(AZ::u32 attribute) const
{
Attribute typedAttribute = static_cast<Attribute>(attribute);
for (const Style* style : m_styles)
{
if (style && style->HasAttribute(typedAttribute))
{
return style->GetAttribute(typedAttribute);
}
}
return QVariant();
}
void ComputedStyle::OnStylesUnloaded()
{
m_styles.clear();
}
#if 0
void ComputedStyle::Dump() const
{
for (const auto& style : m_styles)
{
style->Dump();
}
}
#endif
}
}