You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/GraphCanvas/Code/Source/Widgets/GraphCanvasLabel.cpp

444 lines
13 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 "precompiled.h"
#include <QCoreApplication>
#include <QFont>
#include <QGraphicsItem>
#include <QGraphicsSceneEvent>
#include <QPainter>
#include <qwidget.h>
#include <AzCore/Serialization/EditContext.h>
#include <Widgets/GraphCanvasLabel.h>
#include <GraphCanvas/Editor/GraphCanvasProfiler.h>
#include <GraphCanvas/tools.h>
#include <GraphCanvas/Styling/StyleHelper.h>
namespace GraphCanvas
{
/////////////////////
// GraphCanvasLabel
/////////////////////
GraphCanvasLabel::GraphCanvasLabel(QGraphicsItem* parent)
: QGraphicsWidget(parent)
, m_defaultAlignment(Qt::AlignVCenter | Qt::AlignHCenter)
, m_elide(true)
, m_wrap(false)
, m_allowNewlines(false)
, m_maximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)
, m_minimumSize(0,0)
, m_wrapMode(WrapMode::MaximumWidth)
, m_roundedCornersMode(RoundedCornersMode::AllCorners)
, m_hasBorderOverride(false)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
setGraphicsItem(this);
setFlag(ItemIsMovable, false);
}
void GraphCanvasLabel::SetFontColor(const QColor& color)
{
m_styleHelper.AddAttributeOverride(Styling::Attribute::Color, color);
update();
}
void GraphCanvasLabel::ClearFontColor()
{
m_styleHelper.RemoveAttributeOverride(Styling::Attribute::Color);
update();
}
void GraphCanvasLabel::SetBorderColorOverride(const QBrush& brush)
{
m_hasBorderOverride = true;
m_borderColorOverride = brush;
update();
}
const QBrush& GraphCanvasLabel::GetBorderColorOverride() const
{
return m_borderColorOverride;
}
void GraphCanvasLabel::ClearBorderColorOverride()
{
m_hasBorderOverride = false;
}
void GraphCanvasLabel::SetLabel(const AZStd::string& label, const AZStd::string& translationContext, const AZStd::string& translationKey)
{
TranslationKeyedString keyedString(label, translationContext, translationKey);
SetLabel(keyedString);
}
void GraphCanvasLabel::SetLabel(const TranslationKeyedString& value)
{
AZStd::string displayString = value.GetDisplayString();
if (m_labelText.compare(QString(displayString.c_str())))
{
m_labelText = Tools::qStringFromUtf8(displayString);
UpdateDisplayText();
RefreshDisplay();
}
}
void GraphCanvasLabel::SetSceneStyle(const AZ::EntityId& sceneId, const char* style)
{
m_styleHelper.SetScene(sceneId);
m_styleHelper.SetStyle(style);
UpdateDisplayText();
RefreshDisplay();
}
void GraphCanvasLabel::SetStyle(const AZ::EntityId& entityId, const char* styleElement)
{
m_styleHelper.SetStyle(entityId, styleElement);
UpdateDisplayText();
RefreshDisplay();
}
void GraphCanvasLabel::RefreshDisplay()
{
UpdateDesiredBounds();
updateGeometry();
update();
}
void GraphCanvasLabel::SetWrapMode(WrapMode wrapMode)
{
if (m_wrapMode != wrapMode)
{
m_wrapMode = wrapMode;
UpdateDesiredBounds();
UpdateDisplayText();
RefreshDisplay();
}
}
void GraphCanvasLabel::SetRoundedCornersMode(RoundedCornersMode roundedCornersMode)
{
if (m_roundedCornersMode != roundedCornersMode)
{
m_roundedCornersMode = roundedCornersMode;
update();
}
}
QRectF GraphCanvasLabel::GetDisplayedSize() const
{
return m_displayedSize;
}
void GraphCanvasLabel::SetElide(bool elide)
{
if (m_elide != elide)
{
m_elide = elide;
RefreshDisplay();
}
}
void GraphCanvasLabel::SetWrap(bool wrap)
{
if (m_wrap != wrap)
{
m_wrap = wrap;
RefreshDisplay();
}
}
void GraphCanvasLabel::SetAllowNewlines(bool allow)
{
if (m_allowNewlines != allow)
{
m_allowNewlines = allow;
UpdateDisplayText();
RefreshDisplay();
}
}
void GraphCanvasLabel::SetDefaultAlignment(Qt::Alignment defaultAlignment)
{
m_defaultAlignment = defaultAlignment;
update();
}
Styling::StyleHelper& GraphCanvasLabel::GetStyleHelper()
{
return m_styleHelper;
}
const Styling::StyleHelper& GraphCanvasLabel::GetStyleHelper() const
{
return m_styleHelper;
}
void GraphCanvasLabel::UpdateDisplayText()
{
qreal padding = m_styleHelper.GetAttribute(Styling::Attribute::Padding, 2.0f);
QFontMetrics metrics(m_styleHelper.GetFont());
QRectF innerBounds = boundingRect();
innerBounds = innerBounds.adjusted(padding, padding, -padding, -padding);
QString labelText = m_allowNewlines ? m_labelText : m_labelText.simplified();
if (m_elide)
{
QStringList newlines = labelText.split('\n');
m_displayText.clear();
bool needsNewline = false;
for (const QString& currentLine : newlines)
{
if (needsNewline)
{
m_displayText.append('\n');
}
m_displayText = m_displayText.append(metrics.elidedText(currentLine, Qt::TextElideMode::ElideRight, aznumeric_cast<int>(innerBounds.width())));
needsNewline = true;
}
}
else
{
m_displayText = labelText;
}
}
void GraphCanvasLabel::UpdateDesiredBounds()
{
prepareGeometryChange();
qreal padding = m_styleHelper.GetAttribute(Styling::Attribute::Padding, 2.0f);
QFontMetricsF metrics = QFontMetricsF(m_styleHelper.GetFont());
int flags = m_defaultAlignment;
if (m_wrap)
{
flags = flags | Qt::TextWordWrap;
}
flags = flags & ~Qt::TextSingleLine;
m_maximumSize = m_styleHelper.GetMaximumSize();
QRectF fontRectangle;
if (m_wrapMode == WrapMode::ResizeToContent)
{
fontRectangle = metrics.boundingRect((m_allowNewlines) ? m_labelText : m_labelText.simplified());
}
else
{
QSizeF sizeClamp = maximumSize();
if (m_wrapMode == WrapMode::BoundingWidth)
{
sizeClamp.setWidth(boundingRect().size().width());
}
QRectF maxInnerBounds(rect().topLeft(), sizeClamp);
maxInnerBounds = maxInnerBounds.adjusted(padding, padding, -padding, -padding);
fontRectangle = metrics.boundingRect(maxInnerBounds, flags, (m_allowNewlines) ? m_labelText : m_labelText.simplified());
}
// Horizontal Padding:
// 1 padding for the left side
// 1 padding for the right side
//
// Vertical Padding:
// 1 padding for the top
// 1 padding for the bottom.
m_desiredBounds = fontRectangle.adjusted(0.0f, 0.0f, padding * 2.0f, padding * 2.0f);
// Seem so be an off by 1 error here. So adding one in each direction to my desired size to stop text from being clipped.
m_desiredBounds.adjust(-1.0, -1.0, 1.0, 1.0);
m_minimumSize = m_styleHelper.GetMinimumSize(m_desiredBounds.size());
if (m_wrapMode == WrapMode::ResizeToContent)
{
// Because the minimum is usually what drives this. We want to ensure the minimum is the bigger
// of the value style or the desired bounds.
if (m_minimumSize.width() < m_desiredBounds.width())
{
m_minimumSize.setWidth(m_desiredBounds.width() + 5);
}
// Maximum Size should act normally and just be the bigger of either its own value or the desired bounds.
if (m_maximumSize.width() < m_desiredBounds.width())
{
m_maximumSize.setWidth(m_desiredBounds.width() + 5);
}
}
updateGeometry();
setCacheMode(QGraphicsItem::CacheMode::DeviceCoordinateCache);
}
bool GraphCanvasLabel::event(QEvent* qEvent)
{
if (qEvent->type() == QEvent::GraphicsSceneResize)
{
UpdateDisplayText();
}
return QGraphicsWidget::event(qEvent);
}
void GraphCanvasLabel::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget /*= nullptr*/)
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
AZ_Warning("GraphCanvasLabel", !(m_elide && m_wrap), "GraphCanvasLabel doesn't support eliding text and word wrapping at the same time.");
painter->save();
// Background
{
m_displayedSize = boundingRect();
QPainterPath path;
qreal borderRadius = m_styleHelper.GetAttribute(Styling::Attribute::BorderRadius, 0);
if (borderRadius == 0)
{
path.addRect(m_displayedSize);
}
else
{
switch (m_roundedCornersMode)
{
case RoundedCornersMode::AllCorners:
{
path.addRoundedRect(m_displayedSize, borderRadius, borderRadius);
}
break;
case RoundedCornersMode::LeftCorners:
{
painter->setClipRect(m_displayedSize);
path.addRoundedRect(m_displayedSize.x(),
m_displayedSize.y(),
m_displayedSize.width() + borderRadius * 2,
m_displayedSize.height(),
borderRadius,
borderRadius);
}
break;
case RoundedCornersMode::RightCorners:
{
painter->setClipRect(m_displayedSize);
path.addRoundedRect(m_displayedSize.x() - borderRadius * 2,
m_displayedSize.y(),
m_displayedSize.width() + borderRadius * 2,
m_displayedSize.height(),
borderRadius,
borderRadius);
}
break;
default:
{
AZ_Warning("GraphCanvasLabel", 0, "GraphCanvasLabel has an unsupported rounded corner mode.");
path.addRoundedRect(m_displayedSize, borderRadius, borderRadius);
}
break;
}
}
painter->fillPath(path, m_styleHelper.GetBrush(Styling::Attribute::BackgroundColor));
if (m_styleHelper.HasAttribute(Styling::Attribute::BorderWidth) || m_hasBorderOverride)
{
QPen restorePen = painter->pen();
QPen borderPen = m_styleHelper.GetBorder();
if (m_hasBorderOverride)
{
borderPen.setBrush(m_borderColorOverride);
}
painter->setPen(borderPen);
painter->drawPath(path);
painter->setPen(restorePen);
}
}
// Text.
if (!m_labelText.isEmpty())
{
qreal padding = m_styleHelper.GetAttribute(Styling::Attribute::Padding, 4.0f);
QRectF innerBounds = m_displayedSize;
innerBounds = innerBounds.adjusted(padding, padding, -padding, -padding);
painter->setPen(m_styleHelper.GetColor(Styling::Attribute::Color));
painter->setBrush(QBrush());
painter->setFont(m_styleHelper.GetFont());
Qt::Alignment textAlignment = m_styleHelper.GetTextAlignment(m_defaultAlignment);
QTextOption opt(textAlignment);
opt.setFlags(QTextOption::IncludeTrailingSpaces);
if (m_wrap)
{
opt.setWrapMode(QTextOption::WordWrap);
}
else
{
opt.setWrapMode(QTextOption::NoWrap);
}
painter->drawText(innerBounds, m_displayText, opt);
}
painter->restore();
QGraphicsWidget::paint(painter, option, widget);
}
QSizeF GraphCanvasLabel::sizeHint(Qt::SizeHint which, const QSizeF& constraint /*= QSizeF()*/) const
{
switch (which)
{
case Qt::MinimumSize:
return m_minimumSize;
case Qt::PreferredSize:
return m_desiredBounds.size();
case Qt::MaximumSize:
return m_maximumSize;
default:
return QGraphicsWidget::sizeHint(which, constraint);
}
return QGraphicsWidget::sizeHint(which, constraint);
}
}