Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,373 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Tooltip that displays bitmap.
#include "EditorDefs.h"
#include "BitmapToolTip.h"
// Qt
#include <QVBoxLayout>
// Editor
#include "Util/Image.h"
#include "Util/ImageUtil.h"
static const int STATIC_TEXT_C_HEIGHT = 42;
static const int HISTOGRAM_C_HEIGHT = 130;
/////////////////////////////////////////////////////////////////////////////
// CBitmapToolTip
CBitmapToolTip::CBitmapToolTip(QWidget* parent)
: QWidget(parent, Qt::ToolTip)
, m_staticBitmap(new QLabel(this))
, m_staticText(new QLabel(this))
, m_rgbaHistogram(new CImageHistogramCtrl(this))
, m_alphaChannelHistogram(new CImageHistogramCtrl(this))
{
m_nTimer = 0;
m_hToolWnd = nullptr;
m_bShowHistogram = true;
m_bShowFullsize = false;
m_eShowMode = ESHOW_RGB;
connect(&m_timer, &QTimer::timeout, this, &CBitmapToolTip::OnTimer);
auto* layout = new QVBoxLayout(this);
layout->setSizeConstraint(QLayout::SetFixedSize);
layout->addWidget(m_staticBitmap);
layout->addWidget(m_staticText);
auto* histogramLayout = new QHBoxLayout();
histogramLayout->addWidget(m_rgbaHistogram);
histogramLayout->addWidget(m_alphaChannelHistogram);
m_alphaChannelHistogram->setVisible(false);
layout->addLayout(histogramLayout);
setLayout(layout);
}
CBitmapToolTip::~CBitmapToolTip()
{
}
//////////////////////////////////////////////////////////////////////////
void CBitmapToolTip::GetShowMode(EShowMode& eShowMode, bool& bShowInOriginalSize) const
{
bShowInOriginalSize = CheckVirtualKey(Qt::Key_Space);
eShowMode = ESHOW_RGB;
if (m_bHasAlpha)
{
if (CheckVirtualKey(Qt::Key_Control))
{
eShowMode = ESHOW_RGB_ALPHA;
}
else if (CheckVirtualKey(Qt::Key_Alt))
{
eShowMode = ESHOW_ALPHA;
}
else if (CheckVirtualKey(Qt::Key_Shift))
{
eShowMode = ESHOW_RGBA;
}
}
else if (m_bIsLimitedHDR)
{
if (CheckVirtualKey(Qt::Key_Shift))
{
eShowMode = ESHOW_RGBE;
}
}
}
const char* CBitmapToolTip::GetShowModeDescription(EShowMode eShowMode, [[maybe_unused]] bool bShowInOriginalSize) const
{
switch (eShowMode)
{
case ESHOW_RGB:
return "RGB";
case ESHOW_RGB_ALPHA:
return "RGB+A";
case ESHOW_ALPHA:
return "Alpha";
case ESHOW_RGBA:
return "RGBA";
case ESHOW_RGBE:
return "RGBExp";
}
return "";
}
void CBitmapToolTip::RefreshViewmode()
{
LoadImage(m_filename);
if (m_eShowMode == ESHOW_RGB_ALPHA || m_eShowMode == ESHOW_RGBA)
{
m_rgbaHistogram->setVisible(true);
m_alphaChannelHistogram->setVisible(true);
}
else if (m_eShowMode == ESHOW_ALPHA)
{
m_rgbaHistogram->setVisible(false);
m_alphaChannelHistogram->setVisible(true);
}
else
{
m_rgbaHistogram->setVisible(true);
m_alphaChannelHistogram->setVisible(false);
}
}
bool CBitmapToolTip::LoadImage(const QString& imageFilename)
{
EShowMode eShowMode = ESHOW_RGB;
const char* pShowModeDescription = "RGB";
bool bShowInOriginalSize = false;
GetShowMode(eShowMode, bShowInOriginalSize);
pShowModeDescription = GetShowModeDescription(eShowMode, bShowInOriginalSize);
QString convertedFileName = Path::GamePathToFullPath(Path::ReplaceExtension(imageFilename, ".dds"));
// We need to check against both the image filename and the converted filename as it is possible that the
// converted file existed but failed to load previously and we reverted to loading the source asset.
bool alreadyLoadedImage = ((m_filename == convertedFileName) || (m_filename == imageFilename));
if (alreadyLoadedImage && (m_eShowMode == eShowMode) && (m_bShowFullsize == bShowInOriginalSize))
{
return true;
}
CCryFile fileCheck;
if (!fileCheck.Open(convertedFileName.toUtf8().data(), "rb"))
{
// if we didn't find it, then default back to just using what we can find (if any)
convertedFileName = imageFilename;
}
else
{
fileCheck.Close();
}
m_eShowMode = eShowMode;
m_bShowFullsize = bShowInOriginalSize;
CImageEx image;
image.SetHistogramEqualization(CheckVirtualKey(Qt::Key_Shift));
bool loadedRequestedAsset = true;
if (!CImageUtil::LoadImage(convertedFileName, image))
{
//Failed to load the requested asset, let's try loading the source asset if available.
loadedRequestedAsset = false;
if (!CImageUtil::LoadImage(imageFilename, image))
{
m_staticBitmap->clear();
return false;
}
}
QString imginfo;
m_filename = loadedRequestedAsset ? convertedFileName : imageFilename;
m_bHasAlpha = image.HasAlphaChannel();
m_bIsLimitedHDR = image.IsLimitedHDR();
GetShowMode(eShowMode, bShowInOriginalSize);
pShowModeDescription = GetShowModeDescription(eShowMode, bShowInOriginalSize);
if (m_bHasAlpha)
{
imginfo = tr("%1x%2 %3\nShowing %4 (ALT=Alpha, SHIFT=RGBA, CTRL=RGB+A, SPACE=see in original size)");
}
else if (m_bIsLimitedHDR)
{
imginfo = tr("%1x%2 %3\nShowing %4 (SHIFT=see hist.-equalized, SPACE=see in original size)");
}
else
{
imginfo = tr("%1x%2 %3\nShowing %4 (SPACE=see in original size)");
}
imginfo = imginfo.arg(image.GetWidth()).arg(image.GetHeight()).arg(image.GetFormatDescription()).arg(pShowModeDescription);
m_staticText->setText(imginfo);
int w = image.GetWidth();
int h = image.GetHeight();
int multiplier = (m_eShowMode == ESHOW_RGB_ALPHA ? 2 : 1);
int originalW = w * multiplier;
int originalH = h;
if (!bShowInOriginalSize || (w == 0))
{
w = 256;
}
if (!bShowInOriginalSize || (h == 0))
{
h = 256;
}
w *= multiplier;
resize(w + 4, h + 4 + STATIC_TEXT_C_HEIGHT + HISTOGRAM_C_HEIGHT);
setVisible(true);
CImageEx scaledImage;
if (bShowInOriginalSize && (originalW < w))
{
w = originalW;
}
if (bShowInOriginalSize && (originalH < h))
{
h = originalH;
}
scaledImage.Allocate(w, h);
if (m_eShowMode == ESHOW_RGB_ALPHA)
{
CImageUtil::ScaleToDoubleFit(image, scaledImage);
}
else
{
CImageUtil::ScaleToFit(image, scaledImage);
}
if (m_eShowMode == ESHOW_RGB || m_eShowMode == ESHOW_RGBE)
{
scaledImage.SwapRedAndBlue();
scaledImage.FillAlpha();
}
else if (m_eShowMode == ESHOW_ALPHA)
{
for (int hh = 0; hh < scaledImage.GetHeight(); hh++)
{
for (int ww = 0; ww < scaledImage.GetWidth(); ww++)
{
int a = scaledImage.ValueAt(ww, hh) >> 24;
scaledImage.ValueAt(ww, hh) = RGB(a, a, a);
}
}
}
else if (m_eShowMode == ESHOW_RGB_ALPHA)
{
int halfWidth = scaledImage.GetWidth() / 2;
for (int hh = 0; hh < scaledImage.GetHeight(); hh++)
{
for (int ww = 0; ww < halfWidth; ww++)
{
int r = GetRValue(scaledImage.ValueAt(ww, hh));
int g = GetGValue(scaledImage.ValueAt(ww, hh));
int b = GetBValue(scaledImage.ValueAt(ww, hh));
int a = scaledImage.ValueAt(ww, hh) >> 24;
scaledImage.ValueAt(ww, hh) = RGB(b, g, r);
scaledImage.ValueAt(ww + halfWidth, hh) = RGB(a, a, a);
}
}
}
else //if (m_showMode == ESHOW_RGBA)
{
scaledImage.SwapRedAndBlue();
}
QImage qImage(scaledImage.GetWidth(), scaledImage.GetHeight(), QImage::Format_RGB32);
memcpy(qImage.bits(), scaledImage.GetData(), qImage.sizeInBytes());
m_staticBitmap->setPixmap(QPixmap::fromImage(qImage));
if (m_bShowHistogram && scaledImage.GetData())
{
m_rgbaHistogram->ComputeHistogram(image, CImageHistogram::eImageFormat_32BPP_BGRA);
m_rgbaHistogram->setDrawMode(EHistogramDrawMode::OverlappedRGB);
m_alphaChannelHistogram->histogramDisplay()->CopyComputedDataFrom(m_rgbaHistogram->histogramDisplay());
m_alphaChannelHistogram->setDrawMode(EHistogramDrawMode::AlphaChannel);
}
return true;
}
void CBitmapToolTip::OnTimer()
{
/*
if (IsWindowVisible())
{
if (m_bHaveAnythingToRender)
Invalidate();
}
*/
if (m_hToolWnd)
{
QRect toolRc(m_toolRect);
QRect rc = geometry();
QPoint cursorPos = QCursor::pos();
toolRc.moveTopLeft(m_hToolWnd->mapToGlobal(toolRc.topLeft()));
if (!toolRc.contains(cursorPos) && !rc.contains(cursorPos))
{
setVisible(false);
}
else
{
RefreshViewmode();
}
}
}
//////////////////////////////////////////////////////////////////////////
void CBitmapToolTip::showEvent([[maybe_unused]] QShowEvent* event)
{
QPoint cursorPos = QCursor::pos();
move(cursorPos);
m_timer.start(500);
}
//////////////////////////////////////////////////////////////////////////
void CBitmapToolTip::hideEvent([[maybe_unused]] QHideEvent* event)
{
m_timer.stop();
}
//////////////////////////////////////////////////////////////////////////
void CBitmapToolTip::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Control || event->key() == Qt::Key_Alt || event->key() == Qt::Key_Shift)
{
RefreshViewmode();
}
}
void CBitmapToolTip::keyReleaseEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Control || event->key() == Qt::Key_Alt || event->key() == Qt::Key_Shift)
{
RefreshViewmode();
}
}
//////////////////////////////////////////////////////////////////////////
void CBitmapToolTip::SetTool(QWidget* pWnd, const QRect& rect)
{
assert(pWnd);
m_hToolWnd = pWnd;
m_toolRect = rect;
}
#include <Controls/moc_BitmapToolTip.cpp>
@@ -0,0 +1,92 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : Tooltip that displays bitmap.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_BITMAPTOOLTIP_H
#define CRYINCLUDE_EDITOR_CONTROLS_BITMAPTOOLTIP_H
#pragma once
#if !defined(Q_MOC_RUN)
#include "Controls/ImageHistogramCtrl.h"
#include <QLabel>
#include <QTimer>
#endif
//////////////////////////////////////////////////////////////////////////
class CBitmapToolTip
: public QWidget
{
Q_OBJECT
// Construction
public:
enum EShowMode
{
ESHOW_RGB = 0,
ESHOW_ALPHA,
ESHOW_RGBA,
ESHOW_RGB_ALPHA,
ESHOW_RGBE
};
CBitmapToolTip(QWidget* parent = nullptr);
virtual ~CBitmapToolTip();
BOOL Create(const RECT& rect);
// Attributes
public:
// Operations
public:
void RefreshViewmode();
bool LoadImage(const QString& imageFilename);
void SetTool(QWidget* pWnd, const QRect& rect);
// Generated message map functions
protected:
void OnTimer();
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
void showEvent(QShowEvent* event) override;
void hideEvent(QHideEvent* event) override;
private:
void GetShowMode(EShowMode& showMode, bool& showInOriginalSize) const;
const char* GetShowModeDescription(EShowMode showMode, bool showInOriginalSize) const;
QLabel* m_staticBitmap;
QLabel* m_staticText;
QString m_filename;
bool m_bShowHistogram;
EShowMode m_eShowMode;
bool m_bShowFullsize;
bool m_bHasAlpha;
bool m_bIsLimitedHDR;
CImageHistogramCtrl* m_rgbaHistogram;
CImageHistogramCtrl* m_alphaChannelHistogram;
int m_nTimer;
QWidget* m_hToolWnd;
QRect m_toolRect;
QTimer m_timer;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_BITMAPTOOLTIP_H
@@ -0,0 +1,939 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "ColorGradientCtrl.h"
// Qt
#include <QPainter>
#include <QToolTip>
// AzQtComponents
#include <AzQtComponents/Components/Widgets/ColorPicker.h>
#define MIN_TIME_EPSILON 0.01f
//////////////////////////////////////////////////////////////////////////
CColorGradientCtrl::CColorGradientCtrl(QWidget* parent)
: QWidget(parent)
{
m_nActiveKey = -1;
m_nHitKeyIndex = -1;
m_nKeyDrawRadius = 3;
m_bTracking = false;
m_pSpline = 0;
m_fMinTime = -1;
m_fMaxTime = 1;
m_fMinValue = -1;
m_fMaxValue = 1;
m_fTooltipScaleX = 1;
m_fTooltipScaleY = 1;
m_bNoTimeMarker = true;
m_bLockFirstLastKey = false;
m_bNoZoom = true;
ClearSelection();
m_bSelectedKeys.reserve(0);
m_fTimeMarker = -10;
m_grid.zoom.x = 100;
setMouseTracking(true);
}
CColorGradientCtrl::~CColorGradientCtrl()
{
}
/////////////////////////////////////////////////////////////////////////////
// QColorGradientCtrl message handlers
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
QRect rc(QPoint(0, 0), event->size());
m_rcGradient = rc;
m_rcGradient.setHeight(m_rcGradient.height() - 11);
//m_rcGradient.DeflateRect(4,4);
m_grid.rect = m_rcGradient;
if (m_bNoZoom)
{
m_grid.zoom.x = m_grid.rect.width();
}
m_rcKeys = rc;
m_rcKeys.setTop(m_rcKeys.bottom() - 10);
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::SetZoom(float fZoom)
{
m_grid.zoom.x = fZoom;
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::SetOrigin(float fOffset)
{
m_grid.origin.x = fOffset;
}
//////////////////////////////////////////////////////////////////////////
QPoint CColorGradientCtrl::KeyToPoint(int nKey)
{
if (nKey >= 0)
{
return TimeToPoint(m_pSpline->GetKeyTime(nKey));
}
return QPoint(0, 0);
}
//////////////////////////////////////////////////////////////////////////
QPoint CColorGradientCtrl::TimeToPoint(float time)
{
return QPoint(m_grid.WorldToClient(Vec2(time, 0)).x(), m_rcGradient.height() / 2);
QPoint point;
point.rx() = (time - m_fMinTime) * (m_rcGradient.width() / (m_fMaxTime - m_fMinTime)) + m_rcGradient.left();
point.ry() = m_rcGradient.height() / 2;
return point;
}
//////////////////////////////////////////////////////////////////////////
AZ::Color CColorGradientCtrl::TimeToColor(float time)
{
ISplineInterpolator::ValueType val;
m_pSpline->Interpolate(time, val);
const AZ::Color col = ValueToColor(val);
return col;
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::PointToTimeValue(QPoint point, float& time, ISplineInterpolator::ValueType& val)
{
time = XOfsToTime(point.x());
ColorToValue(TimeToColor(time), val);
}
//////////////////////////////////////////////////////////////////////////
float CColorGradientCtrl::XOfsToTime(int x)
{
return m_grid.ClientToWorld(QPoint(x, 0)).x;
// m_fMinTime to m_fMaxTime time range.
float time = m_fMinTime + (float)((m_fMaxTime - m_fMinTime) * (x - m_rcGradient.left())) / m_rcGradient.width();
return time;
}
//////////////////////////////////////////////////////////////////////////
QPoint CColorGradientCtrl::XOfsToPoint(int x)
{
return TimeToPoint(XOfsToTime(x));
}
//////////////////////////////////////////////////////////////////////////
AZ::Color CColorGradientCtrl::XOfsToColor(int x)
{
return TimeToColor(XOfsToTime(x));
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::paintEvent(QPaintEvent* e)
{
QPainter painter(this);
QRect rcClient = rect();
if (m_pSpline)
{
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
}
{
if (!isEnabled())
{
painter.setBrush(palette().button());
painter.drawRect(rcClient);
return;
}
//////////////////////////////////////////////////////////////////////////
// Fill keys backgound.
//////////////////////////////////////////////////////////////////////////
QRect rcKeys = m_rcKeys.intersected(e->rect());
painter.setBrush(palette().button());
painter.drawRect(rcKeys);
//////////////////////////////////////////////////////////////////////////
//Draw Keys and Curve
if (m_pSpline)
{
DrawGradient(e, &painter);
DrawKeys(e, &painter);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::DrawGradient(QPaintEvent* e, QPainter* painter)
{
int cx = m_rcGradient.width();
int cy = m_rcGradient.height();
//Draw Curve
// create and select a thick, white pen
painter->setPen(QPen(QColor(128, 255, 128), 1, Qt::SolidLine));
const QRect rcClip = e->rect().intersected(m_rcGradient);
const int right = rcClip.left() + rcClip.width();
for (int x = rcClip.left(); x < right; x++)
{
const AZ::Color col = XOfsToColor(x);
QPen pen(QColor(col.GetR8(), col.GetG8(), col.GetR8(), col.GetA8()), 1, Qt::SolidLine);
painter->setPen(pen);
painter->drawLine(x, m_rcGradient.top(), x, m_rcGradient.top() + m_rcGradient.height());
}
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::DrawKeys(QPaintEvent* e, QPainter* painter)
{
if (!m_pSpline)
{
return;
}
// create and select a white pen
painter->setPen(QPen(QColor(0, 0, 0), 1, Qt::SolidLine));
QRect rcClip = e->rect();
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
for (int i = 0; i < m_pSpline->GetKeyCount(); i++)
{
float time = m_pSpline->GetKeyTime(i);
QPoint pt = TimeToPoint(time);
if (pt.x() < rcClip.left() - 8 || pt.x() > rcClip.left() + rcClip.width() + 8)
{
continue;
}
const AZ::Color clr = TimeToColor(time);
QBrush brush(QColor(clr.GetR8(), clr.GetG8(), clr.GetB8(), clr.GetA8()));
painter->setBrush(brush);
// Find the midpoints of the top, right, left, and bottom
// of the client area. They will be the vertices of our polygon.
QPoint pts[3];
pts[0].rx() = pt.x();
pts[0].ry() = m_rcKeys.top() + 1;
pts[1].rx() = pt.x() - 5;
pts[1].ry() = m_rcKeys.top() + 8;
pts[2].rx() = pt.x() + 5;
pts[2].ry() = m_rcKeys.top() + 8;
painter->drawPolygon(pts, 3);
if (m_bSelectedKeys[i])
{
QPen pen(QColor(200, 0, 0), 1, Qt::SolidLine);
QPen oldPen = painter->pen();
painter->setPen(pen);
painter->drawPolygon(pts, 3);
painter->setPen(oldPen);
}
}
if (!m_bNoTimeMarker)
{
QPen timePen(QColor(255, 0, 255), 1, Qt::SolidLine);
painter->setPen(timePen);
QPoint pt = TimeToPoint(m_fTimeMarker);
painter->drawLine(pt.x(), m_rcGradient.top() + 1, pt.x(), m_rcGradient.bottom() - 1);
}
}
void CColorGradientCtrl::UpdateTooltip(QPoint pos)
{
if (m_nHitKeyIndex >= 0)
{
float time = m_pSpline->GetKeyTime(m_nHitKeyIndex);
ISplineInterpolator::ValueType val;
m_pSpline->GetKeyValue(m_nHitKeyIndex, val);
AZ::Color col = TimeToColor(time);
int cont_s = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_IN_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2;
int cont_d = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_OUT_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2;
QString tipText(tr("%1 : %2,%3,%4 [%5,%6]").arg(time * m_fTooltipScaleX, 0, 'f', 2).arg(col.GetR8()).arg(col.GetG8()).arg(col.GetB8()).arg(cont_s).arg(cont_d));
const QPoint globalPos = mapToGlobal(pos);
QToolTip::showText(mapToGlobal(pos), tipText, this, QRect(globalPos, QSize(1, 1)));
}
}
/////////////////////////////////////////////////////////////////////////////
//Mouse Message Handlers
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
OnLButtonDown(event);
}
else if (event->button() == Qt::RightButton)
{
OnRButtonDown(event);
}
}
void CColorGradientCtrl::OnLButtonDown([[maybe_unused]] QMouseEvent* event)
{
if (m_bTracking)
{
return;
}
if (!m_pSpline)
{
return;
}
setFocus();
switch (m_hitCode)
{
case HIT_KEY:
StartTracking();
SetActiveKey(m_nHitKeyIndex);
break;
/*
case HIT_SPLINE:
{
// Cycle the spline slope of the nearest key.
int flags = m_pSpline->GetKeyFlags(m_nHitKeyIndex);
if (m_nHitKeyDist < 0)
// Toggle left side.
flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_IN_SHIFT;
if (m_nHitKeyDist > 0)
// Toggle right side.
flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_OUT_SHIFT;
m_pSpline->SetKeyFlags(m_nHitKeyIndex, flags);
m_pSpline->Update();
SetActiveKey(-1);
SendNotifyEvent( CLRGRDN_CHANGE );
if (m_updateCallback)
m_updateCallback(this);
break;
}
*/
case HIT_NOTHING:
SetActiveKey(-1);
break;
}
update();
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::OnRButtonDown([[maybe_unused]] QMouseEvent* event)
{
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::mouseDoubleClickEvent(QMouseEvent* event)
{
if (!m_pSpline)
{
return;
}
if (event->button() != Qt::LeftButton)
{
return;
}
switch (m_hitCode)
{
case HIT_SPLINE:
{
int iIndex = InsertKey(event->pos());
SetActiveKey(iIndex);
EditKey(iIndex);
update();
}
break;
case HIT_KEY:
{
EditKey(m_nHitKeyIndex);
}
break;
}
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::mouseMoveEvent(QMouseEvent* event)
{
if (!m_pSpline)
{
return;
}
if (!m_bTracking)
{
switch (HitTest(event->pos()))
{
case HIT_SPLINE:
{
setCursor(CMFCUtils::LoadCursor(IDC_ARRWHITE));
} break;
case HIT_KEY:
{
setCursor(CMFCUtils::LoadCursor(IDC_ARRBLCK));
} break;
default:
break;
}
}
if (m_bTracking)
{
TrackKey(event->pos());
}
if (m_bTracking || m_nHitKeyIndex >= 0)
{
UpdateTooltip(event->pos());
}
else
{
QToolTip::hideText();
}
}
void CColorGradientCtrl::mouseReleaseEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
OnLButtonUp(event);
}
else if (event->button() == Qt::RightButton)
{
OnRButtonUp(event);
}
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::OnLButtonUp(QMouseEvent* event)
{
if (!m_pSpline)
{
return;
}
if (m_bTracking)
{
StopTracking(event->pos());
}
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::OnRButtonUp([[maybe_unused]] QMouseEvent* event)
{
if (!m_pSpline)
{
return;
}
}
/////////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::SetActiveKey(int nIndex)
{
ClearSelection();
//Activate New Key
if (nIndex >= 0)
{
m_bSelectedKeys[nIndex] = true;
}
m_nActiveKey = nIndex;
update();
SendNotifyEvent(CLRGRDN_ACTIVE_KEY_CHANGE);
}
/////////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::SetSpline(ISplineInterpolator* pSpline, BOOL bRedraw)
{
if (pSpline != m_pSpline)
{
//if (pSpline && pSpline->GetNumDimensions() != 3)
//return;
m_pSpline = pSpline;
m_nActiveKey = -1;
}
ClearSelection();
if (bRedraw)
{
update();
}
}
//////////////////////////////////////////////////////////////////////////
ISplineInterpolator* CColorGradientCtrl::GetSpline()
{
return m_pSpline;
}
/////////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::keyPressEvent(QKeyEvent* event)
{
BOOL bProcessed = false;
if (m_nActiveKey != -1 && m_pSpline)
{
switch (event->key())
{
case Qt::Key_Delete:
{
RemoveKey(m_nActiveKey);
bProcessed = true;
} break;
case Qt::Key_Up:
{
CUndo undo("Move Spline Key");
QPoint point = KeyToPoint(m_nActiveKey);
point.rx() -= 1;
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
TrackKey(point);
bProcessed = true;
} break;
case Qt::Key_Down:
{
CUndo undo("Move Spline Key");
QPoint point = KeyToPoint(m_nActiveKey);
point.rx() += 1;
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
TrackKey(point);
bProcessed = true;
} break;
case Qt::Key_Left:
{
CUndo undo("Move Spline Key");
QPoint point = KeyToPoint(m_nActiveKey);
point.rx() -= 1;
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
TrackKey(point);
bProcessed = true;
} break;
case Qt::Key_Right:
{
CUndo undo("Move Spline Key");
QPoint point = KeyToPoint(m_nActiveKey);
point.rx() += 1;
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
TrackKey(point);
bProcessed = true;
} break;
default:
break; //do nothing
}
update();
}
event->setAccepted(bProcessed);
}
//////////////////////////////////////////////////////////////////////////////
CColorGradientCtrl::EHitCode CColorGradientCtrl::HitTest(QPoint point)
{
if (!m_pSpline)
{
return HIT_NOTHING;
}
ISplineInterpolator::ValueType val;
float time;
PointToTimeValue(point, time, val);
QRect rc = rect();
m_nHitKeyIndex = -1;
if (rc.contains(point))
{
m_nHitKeyDist = 0xFFFF;
m_hitCode = HIT_SPLINE;
for (int i = 0; i < m_pSpline->GetKeyCount(); i++)
{
QPoint splinePt = TimeToPoint(m_pSpline->GetKeyTime(i));
if (abs(point.x() - splinePt.x()) < abs(m_nHitKeyDist))
{
m_nHitKeyIndex = i;
m_nHitKeyDist = point.x() - splinePt.x();
}
}
if (abs(m_nHitKeyDist) < 4)
{
m_hitCode = HIT_KEY;
}
}
else
{
m_hitCode = HIT_NOTHING;
}
return m_hitCode;
}
///////////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::StartTracking()
{
m_bTracking = true;
GetIEditor()->BeginUndo();
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
setCursor(CMFCUtils::LoadCursor(IDC_ARRBLCKCROSS));
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::TrackKey(QPoint point)
{
if (point.x() < m_rcGradient.left() || point.y() > m_rcGradient.right())
{
return;
}
int nKey = m_nHitKeyIndex;
if (nKey >= 0)
{
ISplineInterpolator::ValueType val;
float time;
PointToTimeValue(point, time, val);
// Clamp to min/max time.
if (time < m_fMinTime || time > m_fMaxTime)
{
return;
}
int i;
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
{
// Switch to next key.
if ((m_pSpline->GetKeyTime(i) < time && i > nKey) ||
(m_pSpline->GetKeyTime(i) > time && i < nKey))
{
m_pSpline->SetKeyTime(nKey, time);
m_pSpline->Update();
SetActiveKey(i);
m_nHitKeyIndex = i;
return;
}
}
if (!m_bLockFirstLastKey || (nKey != 0 && nKey != m_pSpline->GetKeyCount() - 1))
{
m_pSpline->SetKeyTime(nKey, time);
m_pSpline->Update();
}
SendNotifyEvent(CLRGRDN_CHANGE);
if (m_updateCallback)
{
m_updateCallback(this);
}
update();
}
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::StopTracking(QPoint point)
{
if (!m_bTracking)
{
return;
}
GetIEditor()->AcceptUndo("Spline Move");
if (m_nHitKeyIndex >= 0)
{
QRect rc = rect();
rc = rc.marginsAdded(QMargins(100, 100, 100, 100));
if (!rc.contains(point))
{
RemoveKey(m_nHitKeyIndex);
}
}
m_bTracking = false;
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::EditKey(int nKey)
{
if (!m_pSpline)
{
return;
}
if (nKey < 0 || nKey >= m_pSpline->GetKeyCount())
{
return;
}
SetActiveKey(nKey);
ISplineInterpolator::ValueType val;
m_pSpline->GetKeyValue(nKey, val);
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
AzQtComponents::ColorPicker dlg(AzQtComponents::ColorPicker::Configuration::RGB);
dlg.setCurrentColor(ValueToColor(val));
dlg.setSelectedColor(ValueToColor(val));
connect(&dlg, &AzQtComponents::ColorPicker::currentColorChanged, this, &CColorGradientCtrl::OnKeyColorChanged);
if (dlg.exec() == QDialog::Accepted)
{
CUndo undo("Modify Gradient Color");
OnKeyColorChanged(dlg.selectedColor());
}
else
{
OnKeyColorChanged(ValueToColor(val));
}
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::OnKeyColorChanged(const AZ::Color& color)
{
int nKey = m_nActiveKey;
if (!m_pSpline)
{
return;
}
if (nKey < 0 || nKey >= m_pSpline->GetKeyCount())
{
return;
}
ISplineInterpolator::ValueType val;
ColorToValue(color, val);
m_pSpline->SetKeyValue(nKey, val);
update();
if (m_bLockFirstLastKey)
{
if (nKey == 0)
{
m_pSpline->SetKeyValue(m_pSpline->GetKeyCount() - 1, val);
}
else if (nKey == m_pSpline->GetKeyCount() - 1)
{
m_pSpline->SetKeyValue(0, val);
}
}
m_pSpline->Update();
SendNotifyEvent(CLRGRDN_CHANGE);
if (m_updateCallback)
{
m_updateCallback(this);
}
GetIEditor()->UpdateViews(eRedrawViewports);
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::RemoveKey(int nKey)
{
if (!m_pSpline)
{
return;
}
if (m_bLockFirstLastKey)
{
if (nKey == 0 || nKey == m_pSpline->GetKeyCount() - 1)
{
return;
}
}
CUndo undo("Remove Spline Key");
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
m_nActiveKey = -1;
m_nHitKeyIndex = -1;
if (m_pSpline)
{
m_pSpline->RemoveKey(nKey);
m_pSpline->Update();
}
SendNotifyEvent(CLRGRDN_CHANGE);
if (m_updateCallback)
{
m_updateCallback(this);
}
update();
}
//////////////////////////////////////////////////////////////////////////
int CColorGradientCtrl::InsertKey(QPoint point)
{
CUndo undo("Spline Insert Key");
ISplineInterpolator::ValueType val;
float time;
PointToTimeValue(point, time, val);
if (time < m_fMinTime || time > m_fMaxTime)
{
return -1;
}
int i;
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
{
// Skip if any key already have time that is very close.
if (fabs(m_pSpline->GetKeyTime(i) - time) < MIN_TIME_EPSILON)
{
return i;
}
}
SendNotifyEvent(CLRGRDN_BEFORE_CHANGE);
m_pSpline->InsertKey(time, val);
m_pSpline->Interpolate(time, val);
ClearSelection();
update();
SendNotifyEvent(CLRGRDN_CHANGE);
if (m_updateCallback)
{
m_updateCallback(this);
}
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
{
// Find key with added time.
if (m_pSpline->GetKeyTime(i) == time)
{
return i;
}
}
return -1;
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::ClearSelection()
{
m_nActiveKey = -1;
if (m_pSpline)
{
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
}
for (int i = 0; i < (int)m_bSelectedKeys.size(); i++)
{
m_bSelectedKeys[i] = false;
}
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::SetTimeMarker(float fTime)
{
if (!m_pSpline)
{
return;
}
{
QPoint pt = TimeToPoint(m_fTimeMarker);
QRect rc = QRect(pt.x(), m_rcGradient.top(), 0, m_rcGradient.bottom() - m_rcGradient.top()).normalized();
rc += QMargins(1, 0, 1, 0);
update(rc);
}
{
QPoint pt = TimeToPoint(fTime);
QRect rc = QRect(pt.x(), m_rcGradient.top(), 0, m_rcGradient.bottom() - m_rcGradient.top()).normalized();
rc += QMargins(1, 0, 1, 0);
update(rc);
}
m_fTimeMarker = fTime;
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::SendNotifyEvent(int nEvent)
{
switch (nEvent)
{
case CLRGRDN_BEFORE_CHANGE:
emit beforeChange();
break;
case CLRGRDN_CHANGE:
emit change();
break;
case CLRGRDN_ACTIVE_KEY_CHANGE:
emit activeKeyChange();
break;
}
}
//////////////////////////////////////////////////////////////////////////
AZ::Color CColorGradientCtrl::ValueToColor(ISplineInterpolator::ValueType val)
{
const AZ::Color color(val[0], val[1], val[2], 1.0);
return color.LinearToGamma();
}
//////////////////////////////////////////////////////////////////////////
void CColorGradientCtrl::ColorToValue(const AZ::Color& col, ISplineInterpolator::ValueType& val)
{
const AZ::Color colLin = col.GammaToLinear();
val[0] = colLin.GetR();
val[1] = colLin.GetG();
val[2] = colLin.GetB();
val[3] = 0;
}
void CColorGradientCtrl::SetNoTimeMarker(bool noTimeMarker)
{
m_bNoTimeMarker = noTimeMarker;
update();
}
#include <Controls/moc_ColorGradientCtrl.cpp>
@@ -0,0 +1,172 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_COLORGRADIENTCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_COLORGRADIENTCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <ISplines.h>
#include "Controls/WndGridHelper.h"
#endif
namespace AZ
{
class Color;
}
// Notify event sent when spline is being modified.
#define CLRGRDN_CHANGE (0x0001)
// Notify event sent just before when spline is modified.
#define CLRGRDN_BEFORE_CHANGE (0x0002)
// Notify event sent when the active key changes
#define CLRGRDN_ACTIVE_KEY_CHANGE (0x0003)
//////////////////////////////////////////////////////////////////////////
// Spline control.
//////////////////////////////////////////////////////////////////////////
class CColorGradientCtrl
: public QWidget
{
Q_OBJECT
public:
CColorGradientCtrl(QWidget* parent = nullptr);
virtual ~CColorGradientCtrl();
//Key functions
int GetActiveKey() { return m_nActiveKey; };
void SetActiveKey(int nIndex);
int InsertKey(QPoint point);
// Turns on/off zooming and scroll support.
void SetNoZoom([[maybe_unused]] bool bNoZoom) { m_bNoZoom = false; };
void SetTimeRange(float tmin, float tmax) { m_fMinTime = tmin; m_fMaxTime = tmax; }
void SetValueRange(float tmin, float tmax) { m_fMinValue = tmin; m_fMaxValue = tmax; }
void SetTooltipValueScale(float x, float y) { m_fTooltipScaleX = x; m_fTooltipScaleY = y; };
// Lock value of first and last key to be the same.
void LockFirstAndLastKeys(bool bLock) { m_bLockFirstLastKey = bLock; }
void SetSpline(ISplineInterpolator* pSpline, BOOL bRedraw = FALSE);
ISplineInterpolator* GetSpline();
void SetTimeMarker(float fTime);
// Zoom in pixels per time unit.
void SetZoom(float fZoom);
void SetOrigin(float fOffset);
typedef Functor1<CColorGradientCtrl*> UpdateCallback;
void SetUpdateCallback(const UpdateCallback& cb) { m_updateCallback = cb; };
void SetNoTimeMarker(bool noTimeMarker);
signals:
void change();
void beforeChange();
void activeKeyChange();
protected:
enum EHitCode
{
HIT_NOTHING,
HIT_KEY,
HIT_SPLINE,
};
void paintEvent(QPaintEvent* e);
void resizeEvent(QResizeEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void OnLButtonDown(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event) override;
void OnLButtonUp(QMouseEvent* event);
void OnRButtonUp(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event) override;
void OnRButtonDown(QMouseEvent* event);
void keyPressEvent(QKeyEvent* event) override;
// Drawing functions
void DrawGradient(QPaintEvent* e, QPainter* painter);
void DrawKeys(QPaintEvent* e, QPainter* painter);
void UpdateTooltip(QPoint pos);
EHitCode HitTest(QPoint point);
//Tracking support helper functions
void StartTracking();
void TrackKey(QPoint point);
void StopTracking(QPoint point);
void RemoveKey(int nKey);
void EditKey(int nKey);
QPoint KeyToPoint(int nKey);
QPoint TimeToPoint(float time);
void PointToTimeValue(QPoint point, float& time, ISplineInterpolator::ValueType& val);
float XOfsToTime(int x);
QPoint XOfsToPoint(int x);
AZ::Color XOfsToColor(int x);
AZ::Color TimeToColor(float time);
void ClearSelection();
void SendNotifyEvent(int nEvent);
AZ::Color ValueToColor(ISplineInterpolator::ValueType val);
void ColorToValue(const AZ::Color& col, ISplineInterpolator::ValueType& val);
private:
void OnKeyColorChanged(const AZ::Color& color);
private:
ISplineInterpolator* m_pSpline;
bool m_bAutoDelete;
bool m_bNoZoom;
QRect m_rcClipRect;
QRect m_rcGradient;
QRect m_rcKeys;
QPoint m_hitPoint;
EHitCode m_hitCode;
int m_nHitKeyIndex;
int m_nHitKeyDist;
QPoint m_curvePoint;
float m_fTimeMarker;
int m_nActiveKey;
int m_nKeyDrawRadius;
bool m_bTracking;
float m_fMinTime, m_fMaxTime;
float m_fMinValue, m_fMaxValue;
float m_fTooltipScaleX, m_fTooltipScaleY;
bool m_bLockFirstLastKey;
bool m_bNoTimeMarker;
std::vector<int> m_bSelectedKeys;
UpdateCallback m_updateCallback;
CWndGridHelper m_grid;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_COLORGRADIENTCTRL_H
File diff suppressed because it is too large Load Diff
+208
View File
@@ -0,0 +1,208 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCB_H
#define CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCB_H
#pragma once
#if !defined(Q_MOC_RUN)
#include "Settings.h"
#include <AzToolsFramework/Editor/EditorSettingsAPIBus.h>
#include <QLineEdit>
#include <QPlainTextEdit>
#include <QAbstractTableModel>
#include <QDialog>
#include <QLineEdit>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QScopedPointer>
#include <QStyledItemDelegate>
#endif
class QMenu;
class ConsoleWidget;
class QFocusEvent;
class QTableView;
class CVarBlock;
namespace Ui {
class Console;
}
struct ConsoleLine
{
QString text;
bool newLine;
};
typedef std::deque<ConsoleLine> Lines;
class ConsoleLineEdit
: public QLineEdit
{
Q_OBJECT
public:
explicit ConsoleLineEdit(QWidget* parent = nullptr);
protected:
void mouseDoubleClickEvent(QMouseEvent* ev) override;
void keyPressEvent(QKeyEvent* ev) override;
bool event(QEvent* ev) override;
signals:
void variableEditorRequested();
private:
void DisplayHistory(bool bForward);
void ResetHistoryIndex();
QStringList m_history;
unsigned int m_historyIndex;
bool m_bReusedHistory;
};
class ConsoleTextEdit
: public QPlainTextEdit
{
Q_OBJECT
public:
explicit ConsoleTextEdit(QWidget* parent = nullptr);
virtual bool event(QEvent* theEvent) override;
signals:
void searchBarRequested();
private:
void showContextMenu(const QPoint& pt);
QScopedPointer<QMenu> m_contextMenu;
};
class ConsoleVariableItemDelegate
: public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ConsoleVariableItemDelegate(QObject* parent = nullptr);
// Item delegate overrides for creating the custom editor widget and
// setting/retrieving data to/from it
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void SetVarBlock(CVarBlock* varBlock);
private:
CVarBlock* m_varBlock;
};
class ConsoleVariableModel
: public QAbstractTableModel
{
Q_OBJECT
public:
enum CustomRoles
{
VariableCustomRole = Qt::UserRole
};
explicit ConsoleVariableModel(QObject* parent = nullptr);
// Table model overrides
QVariant data(const QModelIndex& index, int role) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
int rowCount(const QModelIndex& = {}) const override;
int columnCount(const QModelIndex& = {}) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
void SetVarBlock(CVarBlock* varBlock);
void ClearModifiedRows();
private:
CVarBlock* m_varBlock;
QList<int> m_modifiedRows;
};
class ConsoleVariableEditor
: public QWidget
{
Q_OBJECT
public:
explicit ConsoleVariableEditor(QWidget* parent = nullptr);
static void RegisterViewClass();
void HandleVariableRowUpdated(ICVar* pCVar);
protected:
void showEvent(QShowEvent* event) override;
private:
void SetVarBlock(CVarBlock* varBlock);
QTableView* m_tableView;
ConsoleVariableModel* m_model;
ConsoleVariableItemDelegate* m_itemDelegate;
CVarBlock* m_varBlock;
};
class CConsoleSCB
: public QWidget
, private AzToolsFramework::EditorPreferencesNotificationBus::Handler
{
Q_OBJECT
public:
explicit CConsoleSCB(QWidget* parent = nullptr);
~CConsoleSCB();
static void RegisterViewClass();
void SetInputFocus();
void AddToConsole(const QString& text, bool bNewLine);
void FlushText();
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
static CConsoleSCB* GetCreatedInstance();
static void AddToPendingLines(const QString& text, bool bNewLine); // call this function instead of AddToConsole() until an instance of CConsoleSCB exists to prevent messages from getting lost
// EditorPreferencesNotificationBus...
void OnEditorPreferencesChanged() override;
void RefreshStyle();
private Q_SLOTS:
void showVariableEditor();
void toggleConsoleSearch();
void findPrevious();
void findNext();
private:
QScopedPointer<Ui::Console> ui;
int m_richEditTextLength;
Lines m_lines;
static Lines s_pendingLines;
QList<QColor> m_colorTable;
AzToolsFramework::ConsoleColorTheme m_backgroundTheme;
class SearchHighlighter;
SearchHighlighter* m_highlighter;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCB_H
@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/controls/img">
<file alias="cvar_dark.bmp">../res/cvar_dark.bmp</file>
</qresource>
</RCC>
+238
View File
@@ -0,0 +1,238 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Console</class>
<widget class="QWidget" name="Console">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>120</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Console</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="ConsoleTextEdit" name="textEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="findBar" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_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="QLabel" name="label">
<property name="text">
<string>Find: </string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEditFind">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="findPrevButton">
<property name="text">
<string>Find Previous</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="findNextButton">
<property name="text">
<string>Find Next</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="closeButton">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="container2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>20</height>
</size>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</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="QToolButton" name="button">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="findButton">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="ConsoleLineEdit" name="lineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ConsoleLineEdit</class>
<extends>QLineEdit</extends>
<header>Controls/ConsoleSCB.h</header>
</customwidget>
<customwidget>
<class>ConsoleTextEdit</class>
<extends>QPlainTextEdit</extends>
<header>Controls/ConsoleSCB.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>lineEdit</tabstop>
<tabstop>button</tabstop>
<tabstop>textEdit</tabstop>
</tabstops>
<resources>
<include location="ConsoleSCB.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,547 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "ConsoleSCBMFC.h"
#include "PropertiesDialog.h"
#include "QtViewPaneManager.h"
#include "Core/QtEditorApplication.h"
#include <Controls/ui_ConsoleSCBMFC.h>
#include <QtUtil.h>
#include <QtUtilWin.h>
#include <QtCore/QStringList>
#include <QtCore/QScopedPointer>
#include <QtCore/QPoint>
#include <QtGui/QCursor>
#include <QtGui/QMouseEvent>
#include <QtWidgets/QStyle>
#include <QtWidgets/QStyleFactory>
#include <QtWidgets/QMenu>
#include <QtWidgets/QScrollBar>
#include <QtWidgets/QVBoxLayout>
#include <vector>
#include <iostream>
namespace MFC
{
static CPropertiesDialog* gPropertiesDlg = nullptr;
static CString mfc_popup_helper(HWND hwnd, int x, int y);
static CConsoleSCB* s_consoleSCB = nullptr;
static QString RemoveColorCode(const QString& text, int& iColorCode)
{
QString cleanString;
cleanString.reserve(text.size());
const int textSize = text.size();
for (int i = 0; i < textSize; ++i)
{
QChar c = text.at(i);
bool isLast = i == textSize - 1;
if (c == '$' && !isLast && text.at(i + 1).isDigit())
{
if (iColorCode == 0)
{
iColorCode = text.at(i + 1).digitValue();
}
++i;
continue;
}
if (c == '\r' || c == '\n')
{
++i;
continue;
}
cleanString.append(c);
}
return cleanString;
}
ConsoleLineEdit::ConsoleLineEdit(QWidget* parent)
: QLineEdit(parent)
, m_historyIndex(0)
, m_bReusedHistory(false)
{
}
void ConsoleLineEdit::mousePressEvent(QMouseEvent* ev)
{
if (ev->type() == QEvent::MouseButtonPress && ev->button() & Qt::RightButton)
{
Q_EMIT variableEditorRequested();
}
QLineEdit::mousePressEvent(ev);
}
void ConsoleLineEdit::mouseDoubleClickEvent(QMouseEvent* ev)
{
Q_EMIT variableEditorRequested();
}
bool ConsoleLineEdit::event(QEvent* ev)
{
// Tab key doesn't go to keyPressEvent(), must be processed here
if (ev->type() != QEvent::KeyPress)
{
return QLineEdit::event(ev);
}
QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
if (ke->key() != Qt::Key_Tab)
{
return QLineEdit::event(ev);
}
QString inputStr = text();
QString newStr;
QStringList tokens = inputStr.split(" ");
inputStr = tokens.isEmpty() ? QString() : tokens.first();
IConsole* console = GetIEditor()->GetSystem()->GetIConsole();
const bool ctrlPressed = ke->modifiers() & Qt::ControlModifier;
CString cstring = QtUtil::ToCString(inputStr); // TODO: Use QString once the backend stops using QString
if (ctrlPressed)
{
newStr = QtUtil::ToString(console->AutoCompletePrev(cstring));
}
else
{
newStr = QtUtil::ToString(console->ProcessCompletion(cstring));
newStr = QtUtil::ToString(console->AutoComplete(cstring));
if (newStr.isEmpty())
{
newStr = QtUtil::ToQString(GetIEditor()->GetCommandManager()->AutoComplete(QtUtil::ToString(newStr)));
}
}
if (!newStr.isEmpty())
{
newStr += " ";
setText(newStr);
}
deselect();
return true;
}
void ConsoleLineEdit::keyPressEvent(QKeyEvent* ev)
{
IConsole* console = GetIEditor()->GetSystem()->GetIConsole();
auto commandManager = GetIEditor()->GetCommandManager();
console->ResetAutoCompletion();
switch (ev->key())
{
case Qt::Key_Enter:
case Qt::Key_Return:
{
QString str = text().trimmed();
if (!str.isEmpty())
{
if (commandManager->IsRegistered(QtUtil::ToCString(str)))
{
commandManager->Execute(QtUtil::ToString(str));
}
else
{
CLogFile::WriteLine(QtUtil::ToCString(str));
GetIEditor()->GetSystem()->GetIConsole()->ExecuteString(QtUtil::ToCString(str));
}
// If a history command was reused directly via up arrow enter, do not reset history index
if (m_history.size() > 0 && m_historyIndex < m_history.size() && m_history[m_historyIndex] == str)
{
m_bReusedHistory = true;
}
else
{
m_historyIndex = m_history.size();
}
// Do not add the same string if it is the top of the stack, but allow duplicate entries otherwise
if (m_history.isEmpty() || m_history.back() != str)
{
m_history.push_back(str);
if (!m_bReusedHistory)
{
m_historyIndex = m_history.size();
}
}
}
else
{
m_historyIndex = m_history.size();
}
setText(QString());
break;
}
case Qt::Key_AsciiTilde: // ~
case Qt::Key_Agrave: // `
// disable log.
GetIEditor()->ShowConsole(false);
setText(QString());
m_historyIndex = m_history.size();
break;
case Qt::Key_Escape:
setText(QString());
m_historyIndex = m_history.size();
break;
case Qt::Key_Up:
DisplayHistory(false /*bForward*/);
break;
case Qt::Key_Down:
DisplayHistory(true /*bForward*/);
break;
default:
QLineEdit::keyPressEvent(ev);
}
}
void ConsoleLineEdit::DisplayHistory(bool bForward)
{
if (m_history.isEmpty())
{
return;
}
// Immediately after reusing a history entry, ensure up arrow re-displays command just used
if (!m_bReusedHistory || bForward)
{
m_historyIndex = static_cast<unsigned int>(clamp_tpl(static_cast<int>(m_historyIndex) + (bForward ? 1 : -1), 0, m_history.size() - 1));
}
m_bReusedHistory = false;
setText(m_history[m_historyIndex]);
}
ConsoleTextEdit::ConsoleTextEdit(QWidget* parent)
: QTextEdit(parent)
{
}
Lines CConsoleSCB::s_pendingLines;
CConsoleSCB::CConsoleSCB(QWidget* parent)
: QWidget(parent)
, ui(new Ui::ConsoleMFC())
, m_richEditTextLength(0)
, m_backgroundTheme(gSettings.consoleBackgroundColorTheme)
{
m_lines = s_pendingLines;
s_pendingLines.clear();
s_consoleSCB = this;
ui->setupUi(this);
setMinimumHeight(120);
// Setup the color table for the default (light) theme
m_colorTable << QColor(0, 0, 0)
<< QColor(0, 0, 0)
<< QColor(0, 0, 200) // blue
<< QColor(0, 200, 0) // green
<< QColor(200, 0, 0) // red
<< QColor(0, 200, 200) // cyan
<< QColor(128, 112, 0) // yellow
<< QColor(200, 0, 200) // red+blue
<< QColor(0x000080ff)
<< QColor(0x008f8f8f);
OnStyleSettingsChanged();
connect(ui->button, &QPushButton::clicked, this, &CConsoleSCB::showVariableEditor);
connect(ui->lineEdit, &MFC::ConsoleLineEdit::variableEditorRequested, this, &MFC::CConsoleSCB::showVariableEditor);
connect(Editor::EditorQtApplication::instance(), &Editor::EditorQtApplication::skinChanged, this, &MFC::CConsoleSCB::OnStyleSettingsChanged);
if (GetIEditor()->IsInConsolewMode())
{
// Attach / register edit box
//CLogFile::AttachEditBox(m_edit.GetSafeHwnd()); // FIXME
}
}
CConsoleSCB::~CConsoleSCB()
{
s_consoleSCB = nullptr;
delete gPropertiesDlg;
gPropertiesDlg = nullptr;
CLogFile::AttachEditBox(nullptr);
}
void CConsoleSCB::RegisterViewClass()
{
QtViewOptions opts;
opts.preferedDockingArea = Qt::BottomDockWidgetArea;
opts.isDeletable = false;
opts.isStandard = true;
opts.showInMenu = true;
opts.builtInActionId = ID_VIEW_CONSOLEWINDOW;
opts.sendViewPaneNameBackToAmazonAnalyticsServers = true;
RegisterQtViewPane<CConsoleSCB>(GetIEditor(), LyViewPane::Console, LyViewPane::CategoryTools, opts);
}
void CConsoleSCB::OnStyleSettingsChanged()
{
ui->button->setIcon(QIcon(QString(":/controls/img/cvar_dark.bmp")));
// Set the debug/warning text colors appropriately for the background theme
// (e.g. not have black text on black background)
QColor textColor = Qt::black;
m_backgroundTheme = gSettings.consoleBackgroundColorTheme;
if (m_backgroundTheme == SEditorSettings::ConsoleColorTheme::Dark)
{
textColor = Qt::white;
}
m_colorTable[0] = textColor;
m_colorTable[1] = textColor;
QColor bgColor;
if (!GetIEditor()->IsInConsolewMode() && CConsoleSCB::GetCreatedInstance() && m_backgroundTheme == SEditorSettings::ConsoleColorTheme::Dark)
{
bgColor = Qt::black;
}
else
{
bgColor = Qt::white;
}
ui->textEdit->setStyleSheet(QString("QTextEdit{ background: %1 }").arg(bgColor.name(QColor::HexRgb)));
// Clear out the console text when we change our background color since
// some of the previous text colors may not be appropriate for the
// new background color
ui->textEdit->clear();
}
void CConsoleSCB::showVariableEditor()
{
const QPoint cursorPos = QCursor::pos();
CString str = mfc_popup_helper(0, cursorPos.x(), cursorPos.y());
if (!str.IsEmpty())
{
ui->lineEdit->setText(QtUtil::ToQString(str));
}
}
void CConsoleSCB::SetInputFocus()
{
ui->lineEdit->setFocus();
ui->lineEdit->setText(QString());
}
void CConsoleSCB::AddToConsole(const QString& text, bool bNewLine)
{
m_lines.push_back({ text, bNewLine });
FlushText();
}
void CConsoleSCB::FlushText()
{
if (m_lines.empty())
{
return;
}
// Store our current cursor in case we need to restore it, and check if
// the user has scrolled the text edit away from the bottom
const QTextCursor oldCursor = ui->textEdit->textCursor();
QScrollBar* scrollBar = ui->textEdit->verticalScrollBar();
const int oldScrollValue = scrollBar->value();
bool scrolledOffBottom = oldScrollValue != scrollBar->maximum();
ui->textEdit->moveCursor(QTextCursor::End);
QTextCursor textCursor = ui->textEdit->textCursor();
while (!m_lines.empty())
{
ConsoleLine line = m_lines.front();
m_lines.pop_front();
int iColor = 0;
QString text = MFC::RemoveColorCode(line.text, iColor);
if (iColor < 0 || iColor >= m_colorTable.size())
{
iColor = 0;
}
if (line.newLine)
{
text = QtUtil::trimRight(text);
text = "\r\n" + text;
}
QTextCharFormat format;
const QColor color(m_colorTable[iColor]);
format.setForeground(color);
if (iColor != 0)
{
format.setFontWeight(QFont::Bold);
}
textCursor.setCharFormat(format);
textCursor.insertText(text);
}
// If the user has selected some text in the text edit area or has scrolled
// away from the bottom, then restore the previous cursor and keep the scroll
// bar in the same location
if (oldCursor.hasSelection() || scrolledOffBottom)
{
ui->textEdit->setTextCursor(oldCursor);
scrollBar->setValue(oldScrollValue);
}
// Otherwise scroll to the bottom so the latest text can be seen
else
{
scrollBar->setValue(scrollBar->maximum());
}
}
QSize CConsoleSCB::minimumSizeHint() const
{
return QSize(-1, -1);
}
QSize CConsoleSCB::sizeHint() const
{
return QSize(100, 100);
}
/** static */
void CConsoleSCB::AddToPendingLines(const QString& text, bool bNewLine)
{
s_pendingLines.push_back({ text, bNewLine });
}
static CVarBlock* VarBlockFromConsoleVars()
{
IConsole* console = GetIEditor()->GetSystem()->GetIConsole();
std::vector<const char*> cmds;
cmds.resize(console->GetNumVars());
size_t cmdCount = console->GetSortedVars(&cmds[0], cmds.size());
CVarBlock* vb = new CVarBlock;
IVariable* pVariable = 0;
for (int i = 0; i < cmdCount; i++)
{
ICVar* pCVar = console->GetCVar(cmds[i]);
if (!pCVar)
{
continue;
}
int varType = pCVar->GetType();
switch (varType)
{
case CVAR_INT:
pVariable = new CVariable<int>();
pVariable->Set(pCVar->GetIVal());
break;
case CVAR_FLOAT:
pVariable = new CVariable<float>();
pVariable->Set(pCVar->GetFVal());
break;
case CVAR_STRING:
pVariable = new CVariable<CString>();
pVariable->Set(pCVar->GetString());
break;
default:
assert(0);
}
pVariable->SetDescription(pCVar->GetHelp());
pVariable->SetName(cmds[i]);
if (pVariable)
{
vb->AddVariable(pVariable);
}
}
return vb;
}
static void OnConsoleVariableUpdated(IVariable* pVar)
{
if (!pVar)
{
return;
}
CString varName = pVar->GetName();
ICVar* pCVar = GetIEditor()->GetSystem()->GetIConsole()->GetCVar(varName);
if (!pCVar)
{
return;
}
if (pVar->GetType() == IVariable::INT)
{
int val;
pVar->Get(val);
pCVar->Set(val);
}
else if (pVar->GetType() == IVariable::FLOAT)
{
float val;
pVar->Get(val);
pCVar->Set(val);
}
else if (pVar->GetType() == IVariable::STRING)
{
CString val;
pVar->Get(val);
pCVar->Set(val);
}
}
static CString mfc_popup_helper(HWND hwnd, int x, int y)
{
IConsole* console = GetIEditor()->GetSystem()->GetIConsole();
TSmartPtr<CVarBlock> vb = VarBlockFromConsoleVars();
XmlNodeRef node;
if (!gPropertiesDlg)
{
gPropertiesDlg = new CPropertiesDialog("Console Variables", node, AfxGetMainWnd(), true);
}
if (!gPropertiesDlg->m_hWnd)
{
gPropertiesDlg->Create(CPropertiesDialog::IDD, AfxGetMainWnd());
gPropertiesDlg->SetUpdateCallback(functor(OnConsoleVariableUpdated));
}
gPropertiesDlg->ShowWindow(SW_SHOW);
gPropertiesDlg->BringWindowToTop();
gPropertiesDlg->GetPropertyCtrl()->AddVarBlock(vb);
return "";
}
CConsoleSCB* CConsoleSCB::GetCreatedInstance()
{
return s_consoleSCB;
}
} // namespace MFC
#include <Controls/moc_ConsoleSCBMFC.cpp>
@@ -0,0 +1,115 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCBMFC_H
#define CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCBMFC_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QPushButton>
#include "ConsoleSCB.h"
#endif
class QMenu;
class ConsoleWidget;
class QFocusEvent;
namespace Ui {
class ConsoleMFC;
}
namespace MFC
{
struct ConsoleLine
{
QString text;
bool newLine;
};
typedef std::deque<ConsoleLine> Lines;
class ConsoleLineEdit
: public QLineEdit
{
Q_OBJECT
public:
explicit ConsoleLineEdit(QWidget* parent = nullptr);
protected:
void mousePressEvent(QMouseEvent* ev) override;
void mouseDoubleClickEvent(QMouseEvent* ev) override;
void keyPressEvent(QKeyEvent* ev) override;
bool event(QEvent* ev) override;
signals:
void variableEditorRequested();
void setWindowTitle(const QString&);
private:
void DisplayHistory(bool bForward);
QStringList m_history;
unsigned int m_historyIndex;
bool m_bReusedHistory;
};
class ConsoleTextEdit
: public QTextEdit
{
Q_OBJECT
public:
explicit ConsoleTextEdit(QWidget* parent = nullptr);
};
class CConsoleSCB
: public QWidget
{
Q_OBJECT
public:
explicit CConsoleSCB(QWidget* parent = nullptr);
~CConsoleSCB();
static void RegisterViewClass();
void SetInputFocus();
void AddToConsole(const QString& text, bool bNewLine);
void FlushText();
void showPopupAndSetTitle();
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
static CConsoleSCB* GetCreatedInstance();
static void AddToPendingLines(const QString& text, bool bNewLine); // call this function instead of AddToConsole() until an instance of CConsoleSCB exists to prevent messages from getting lost
public Q_SLOTS:
void OnStyleSettingsChanged();
private Q_SLOTS:
void showVariableEditor();
private:
QScopedPointer<Ui::ConsoleMFC> ui;
int m_richEditTextLength;
Lines m_lines;
static Lines s_pendingLines;
QList<QColor> m_colorTable;
SEditorSettings::ConsoleColorTheme m_backgroundTheme;
};
} // namespace MFC
#endif // CRYINCLUDE_EDITOR_CONTROLS_CONSOLESCB_H
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConsoleMFC</class>
<widget class="QWidget" name="Console">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>120</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Console</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QTextEdit" name="textEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="container2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>20</height>
</size>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="button">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="MFC::ConsoleLineEdit" name="lineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>MFC::ConsoleLineEdit</class>
<extends>QLineEdit</extends>
<header>ConsoleSCBMFC.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="ConsoleSCB.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,821 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "CurveEditorCtrl.h"
// Qt
#include <QPainter>
#include <QPainterPath>
namespace CurveEditor
{
const int kHandleSize = 6;
const int kHandleSizeHalf = kHandleSize / 2;
const int kDefaultPadding = 10;
const int kInfoFontSize = 7;
const int kGrid = 4;
const QColor kColor_SelectCross(132, 132, 132);
const QColor kColor_DisabledCross(90, 90, 90);
const QColor kColor_MiddleLines(80, 80, 80);
const QColor kColor_Background(41, 41, 41);
const QColor kColor_Disabled(60, 60, 60);
const QColor kColor_PaddingBorder(128, 128, 128);
const QColor kColor_Text(128, 128, 128);
const QColor kColor_TextCrtPos(187, 187, 187);
const QColor kColor_Curve(255, 0, 0);
const QColor kColor_SelHandle(200, 200, 200);
const QColor kColor_NormalHandle(30, 30, 30);
const QColor kColor_HandleLight(60, 60, 60);
const QColor kColor_HandleShadow(0, 0, 0);
const QColor kColor_MarkLines(0, 255, 0);
}
CCurveEditorCtrl::CCurveEditorCtrl(QWidget* parent)
: QWidget(parent)
{
m_domainMinX = 0.0f;
m_domainMinY = 0.0f;
m_domainMaxX = 1.0f;
m_domainMaxY = 1.0f;
m_bMouseDown = m_bDragging = false;
m_bAllowMouse = true;
m_padding = CurveEditor::kDefaultPadding;
m_flags = eFlag_ShowVerticalRuler
| eFlag_ShowHorizontalRuler
| eFlag_ShowVerticalRulerText
| eFlag_ShowHorizontalRulerText
| eFlag_ShowPaddingBorder
| eFlag_ShowMovingPointAxis
| eFlag_ShowPointHandles;
m_gridSplits.set(CurveEditor::kGrid, CurveEditor::kGrid);
m_fntInfo.setFamily("Arial");
m_fntInfo.setPointSize(CurveEditor::kInfoFontSize);
m_bHovered = false;
m_selCrossPen = QPen(CurveEditor::kColor_SelectCross);
GenerateDefaultCurve();
}
CCurveEditorCtrl::~CCurveEditorCtrl()
{
}
void CCurveEditorCtrl::SetFlags(UINT aFlags)
{
m_flags = aFlags;
}
UINT CCurveEditorCtrl::GetFlags() const
{
return m_flags;
}
bool CCurveEditorCtrl::SetDomainBounds(float aMinX, float aMinY, float aMaxX, float aMaxY)
{
assert(aMinX < aMaxX);
assert(aMinY < aMaxY);
if (aMinX >= aMaxX)
{
return false;
}
if (aMinY >= aMaxY)
{
return false;
}
m_domainMinX = aMinX;
m_domainMinY = aMinY;
m_domainMaxX = aMaxX;
m_domainMaxY = aMaxY;
return true;
}
void CCurveEditorCtrl::GetDomainBounds(float& rMinX, float& rMinY, float& rMaxX, float& rMaxY) const
{
rMinX = m_domainMinX;
rMinY = m_domainMinY;
rMaxX = m_domainMaxX;
rMaxY = m_domainMaxY;
}
void CCurveEditorCtrl::SetGrid(UINT aHorizontalSplits, UINT aVerticalSplits, const QStringList& labelsX, const QStringList& labelsY)
{
assert(aHorizontalSplits);
assert(aVerticalSplits);
if (!aHorizontalSplits)
{
// defaults
aHorizontalSplits = 2;
}
if (!aVerticalSplits)
{
// defaults
aVerticalSplits = 2;
}
m_gridSplits.x = aHorizontalSplits;
m_gridSplits.y = aVerticalSplits;
if (!labelsX.isEmpty())
{
m_labelsX = labelsX;
}
if (!labelsY.isEmpty())
{
m_labelsY = labelsY;
}
}
QPoint CCurveEditorCtrl::ProjectPoint(float x, float y)
{
QPoint pt;
pt.setX(m_padding + (width() - m_padding * 2) * (x - m_domainMinX) / (m_domainMaxX - m_domainMinX));
pt.setY(m_padding + (height() - m_padding * 2) * (1.0f - (y - m_domainMinY) / (m_domainMaxY - m_domainMinY)));
return pt;
}
Vec2 CCurveEditorCtrl::UnprojectPoint(const QPoint& pt)
{
Vec2 vec;
int y = height() - pt.y();
float dx = (width() - m_padding * 2);
float dy = (height() - m_padding * 2);
const float kEpsilon = 0.00000001f;
if (fabs(dx) <= kEpsilon)
{
dx = 1.0f;
}
if (fabs(dy) <= kEpsilon)
{
dy = 1.0f;
}
vec.x = m_domainMinX + (float)(pt.x() - m_padding) / dx * (m_domainMaxX - m_domainMinX);
vec.y = m_domainMinY + (float)(y - m_padding) / dy * (m_domainMaxY - m_domainMinY);
return vec;
}
void CCurveEditorCtrl::SetControlPointCount(UINT aCount)
{
m_points.resize(aCount);
m_projectedPoints.clear();
}
UINT CCurveEditorCtrl::GetControlPointCount() const
{
return m_points.size();
}
void CCurveEditorCtrl::AddControlPoint(const Vec2& rPosition)
{
m_points.push_back(CurvePoint(rPosition.x, rPosition.y));
}
void CCurveEditorCtrl::ClearControlPoints()
{
m_points.clear();
}
void CCurveEditorCtrl::SetControlPoint(UINT aIndex, const Vec2& rPosition)
{
assert(aIndex < m_points.size());
if (aIndex >= m_points.size())
{
return;
}
m_points[aIndex].pos = rPosition;
}
void CCurveEditorCtrl::SetControlPointTangents(UINT aIndex, const Vec2& rLeft, const Vec2& rRight)
{
assert(aIndex < m_points.size());
if (aIndex >= m_points.size())
{
return;
}
m_points[aIndex].tanA = rLeft;
m_points[aIndex].tanB = rRight;
}
void CCurveEditorCtrl::GetControlPoint(UINT aIndex, Vec2& rOutPosition) const
{
assert(aIndex < m_points.size());
if (aIndex >= m_points.size())
{
return;
}
rOutPosition = m_points[aIndex].pos;
}
void CCurveEditorCtrl::GetControlPointTangents(UINT aIndex, Vec2& rOutLeft, Vec2& rOutRight) const
{
assert(aIndex < m_points.size());
if (aIndex >= m_points.size())
{
return;
}
rOutLeft = m_points[aIndex].tanA;
rOutRight = m_points[aIndex].tanB;
}
void CCurveEditorCtrl::paintEvent(QPaintEvent* event)
{
QWidget::paintEvent(event);
QPainter dc(this);
QRect rc = geometry();
QString str;
QRect textSize;
dc.setFont(m_fntInfo);
QFontMetrics fntMetrics(m_fntInfo);
if (m_flags & eFlag_Disabled)
{
// If disabled, just draw a blank square.
dc.fillRect(rc, CurveEditor::kColor_Disabled);
dc.setPen(CurveEditor::kColor_DisabledCross);
dc.drawLine(0, 0, rc.width(), rc.height());
dc.drawLine(rc.width(), 0, 0, rc.height());
return;
}
dc.fillRect(rc, CurveEditor::kColor_Background);
dc.setPen(CurveEditor::kColor_MiddleLines);
if (m_flags & eFlag_ShowVerticalRuler)
{
float y = m_domainMinY;
float grid = (m_domainMaxY - m_domainMinY) / m_gridSplits.y;
QPoint p;
for (int i = 0; i <= m_gridSplits.y; ++i)
{
p = ProjectPoint(0, y);
dc.drawLine(m_padding, p.y(), rc.width() - m_padding, p.y());
if (m_flags & eFlag_ShowVerticalRulerText)
{
if (m_labelsY.empty())
{
str.asprintf("%0.2f", y);
}
else
{
str = m_labelsY[i];
}
textSize = fntMetrics.tightBoundingRect(str);
dc.drawText(2, p.y(), str);
}
y += grid;
}
}
if (m_flags & eFlag_ShowHorizontalRuler)
{
float x = m_domainMinX;
float grid = (m_domainMaxX - m_domainMinX) / m_gridSplits.x;
QPoint p;
for (int i = 0; i <= m_gridSplits.x; ++i)
{
p = ProjectPoint(x, 0);
dc.drawLine(p.x(), m_padding, p.x(), rc.height() - m_padding);
if (m_flags & eFlag_ShowHorizontalRulerText)
{
if (m_labelsX.empty())
{
str.asprintf("%0.2f", x);
}
else
{
str = m_labelsX[i];
}
textSize = fntMetrics.tightBoundingRect(str);
p.setX(p.x() + 2);
if (p.x() + textSize.width() > width())
{
p.setX(width() - textSize.width());
}
dc.drawText(p.x(), height() - m_padding + textSize.height() + 2, str);
}
x += grid;
}
}
dc.setPen(CurveEditor::kColor_MarkLines);
if (m_flags & eFlag_ShowVerticalRuler)
{
QPoint p;
for (size_t i = 0; i < m_marksY.size(); ++i)
{
float v = m_marksY[i];
if (v < m_domainMinY || v > m_domainMaxY)
{
continue;
}
p = ProjectPoint(0, v);
dc.drawLine(m_padding, p.y(), width() - m_padding, p.y());
}
}
if (m_flags & eFlag_ShowHorizontalRuler)
{
QPoint p;
for (size_t i = 0; i < m_marksX.size(); ++i)
{
float v = m_marksX[i];
if (v < m_domainMinX || v > m_domainMaxX)
{
continue;
}
p = ProjectPoint(v, 0);
dc.drawLine(p.x(), m_padding, p.x(), height() - m_padding);
}
}
if (m_flags & eFlag_ShowPaddingBorder)
{
dc.setPen(CurveEditor::kColor_PaddingBorder);
dc.drawRect(m_padding, m_padding, width() - m_padding * 2, height() - m_padding * 2);
}
if (m_bDragging
&& !m_selectedIndices.empty()
&& (m_flags & eFlag_ShowMovingPointAxis))
{
const Vec2& crtPos = m_points[m_selectedIndices[0]].pos;
dc.setBrush(CurveEditor::kColor_TextCrtPos);
str.asprintf("(%0.2f,%0.2f)", crtPos.x, crtPos.y);
textSize = fntMetrics.tightBoundingRect(str);
const int kOffsetFromPointer = 5;
QPoint txtPos(m_lastMousePoint.x() + kOffsetFromPointer, m_lastMousePoint.y() + kOffsetFromPointer);
if (txtPos.x() + textSize.width() > width())
{
txtPos.setX(width() - textSize.width());
}
if (txtPos.y() + textSize.height() > height())
{
txtPos.setY(height() - textSize.height());
}
dc.drawText(txtPos, str);
}
ComputeTangents();
UpdateProjectedPoints();
// for curve debug, tangents poly, don't delete
// dc.setPen(Qt::black);
// dc.drawPolyline(m_projectedPoints.data(), m_projectedPoints.size());
dc.setPen(CurveEditor::kColor_Curve);
// curve
QPainterPath bezierPath;
bezierPath.moveTo(m_projectedPoints[0]);
for (int i = 1; i < m_projectedPoints.size(); i += 3)
{
bezierPath.cubicTo(m_projectedPoints[i], m_projectedPoints[i + 1], m_projectedPoints[i + 2]);
}
dc.drawPath(bezierPath);
// curve control point handles
if (m_flags & eFlag_ShowPointHandles)
{
for (size_t i = 0; i < m_points.size(); ++i)
{
QPoint ptProj = ProjectPoint(m_points[i].pos.x, m_points[i].pos.y);
QRect rcHandle(0, 0, CurveEditor::kHandleSize, CurveEditor::kHandleSize);
rcHandle.moveCenter(ptProj);
std::vector<int>::iterator iter =
std::find(m_selectedIndices.begin(), m_selectedIndices.end(), i);
bool bSelected = (iter != m_selectedIndices.end());
if (bSelected && m_bDragging)
{
dc.setPen(m_selCrossPen);
dc.drawLine(0, ptProj.y(), width(), ptProj.y());
dc.drawLine(ptProj.x(), 0, ptProj.x(), height());
}
dc.fillRect(rcHandle, bSelected
? CurveEditor::kColor_SelHandle
: CurveEditor::kColor_NormalHandle);
dc.setPen(CurveEditor::kColor_HandleLight);
dc.drawLine(ptProj.x() - CurveEditor::kHandleSizeHalf, ptProj.y() - CurveEditor::kHandleSizeHalf,
ptProj.x() - CurveEditor::kHandleSizeHalf, ptProj.y() + CurveEditor::kHandleSizeHalf);
dc.drawLine(ptProj.x() - CurveEditor::kHandleSizeHalf, ptProj.y() + CurveEditor::kHandleSizeHalf,
ptProj.x() + CurveEditor::kHandleSizeHalf, ptProj.y() + CurveEditor::kHandleSizeHalf);
dc.setPen(CurveEditor::kColor_HandleShadow);
dc.drawLine(ptProj.x() + CurveEditor::kHandleSizeHalf, ptProj.y() + CurveEditor::kHandleSizeHalf,
ptProj.x() + CurveEditor::kHandleSizeHalf, ptProj.y() - CurveEditor::kHandleSizeHalf);
dc.drawLine(ptProj.x() + CurveEditor::kHandleSizeHalf, ptProj.y() - CurveEditor::kHandleSizeHalf,
ptProj.x() - CurveEditor::kHandleSizeHalf, ptProj.y() - CurveEditor::kHandleSizeHalf);
}
}
}
void CCurveEditorCtrl::ComputeTangents()
{
for (size_t i = 0; i < m_points.size(); ++i)
{
m_points[i].tanA = m_points[i].pos;
m_points[i].tanB = m_points[i].pos;
}
int maxIndex = m_points.size() - 1;
for (size_t i = 0; i < m_points.size(); ++i)
{
if (i > maxIndex)
{
break;
}
Vec2& p2 = m_points[i].pos;
Vec2& back = m_points[i].tanA;
Vec2& forw = m_points[i].tanB;
const float kEpsilon = 0.000001f;
// first point
if (i == 0)
{
back = p2;
if (maxIndex == 1)
{
Vec2& p3 = m_points[i + 1].pos;
forw = p2 + (p3 - p2) / 3.0f;
}
else if (maxIndex > 0)
{
Vec2& p3 = m_points[i + 1].pos;
Vec2& pb3 = m_points[i + 1].tanA;
float lenOsn = (pb3 - p2).GetLength();
float lenb = (p3 - p2).GetLength();
if (lenOsn > kEpsilon && lenb > kEpsilon)
{
forw = p2 + (pb3 - p2) / (lenOsn / lenb * 3.0f);
}
else
{
forw = p2;
}
}
}
if (i == maxIndex)
{
forw = p2;
if (i > 0)
{
Vec2& p1 = m_points[i - 1].pos;
Vec2& pf1 = m_points[i - 1].tanB;
float lenOsn = (pf1 - p2).GetLength();
float lenf = (p1 - p2).GetLength();
if (lenOsn > kEpsilon && lenf > kEpsilon)
{
back = p2 + (pf1 - p2) / (lenOsn / lenf * 3.0f);
}
else
{
back = p2;
}
}
}
else if (i >= 1 && i <= maxIndex - 1)
{
Vec2& p1 = m_points[i - 1].pos;
Vec2& p3 = m_points[i + 1].pos;
float lenOsn = (p3 - p1).GetLength();
float lenb = (p1 - p2).GetLength();
float lenf = (p3 - p2).GetLength();
if (lenOsn > kEpsilon
&& lenf > kEpsilon
&& lenb > kEpsilon)
{
back = p2 + (p1 - p3) * (lenb / lenOsn / 3.0f);
forw = p2 + (p3 - p1) * (lenf / lenOsn / 3.0f);
}
}
ClampToDomain(back);
ClampToDomain(forw);
}
// fix tangents in relation of one to another
for (size_t i = 0; i < m_points.size(); ++i)
{
Vec2& p = m_points[i].pos;
Vec2& tanA = m_points[i].tanA;
Vec2& tanB = m_points[i].tanB;
if (i < m_points.size() - 1)
{
if (tanB.x > m_points[i + 1].tanA.x)
{
tanB.x = (m_points[i + 1].pos.x + p.x) * 0.5f;
}
}
if (i > 0)
{
if (tanA.x < m_points[i - 1].tanB.x)
{
tanA.x = (m_points[i - 1].pos.x + p.x) * 0.5f;
}
}
}
}
void CCurveEditorCtrl::UpdateProjectedPoints()
{
m_projectedPoints.resize(m_points.size() * 3 - 2);
int numPts = 0;
for (size_t i = 0; i < m_points.size(); ++i)
{
if (i == 0)
{
m_projectedPoints[numPts++] = ProjectPoint(m_points[i].pos.x, m_points[i].pos.y);
m_projectedPoints[numPts++] = ProjectPoint(m_points[i].tanB.x, m_points[i].tanB.y);
}
else if (i == m_points.size() - 1)
{
m_projectedPoints[numPts++] = ProjectPoint(m_points[i].tanA.x, m_points[i].tanA.y);
m_projectedPoints[numPts++] = ProjectPoint(m_points[i].pos.x, m_points[i].pos.y);
}
else
{
m_projectedPoints[numPts++] = ProjectPoint(m_points[i].tanA.x, m_points[i].tanA.y);
m_projectedPoints[numPts++] = ProjectPoint(m_points[i].pos.x, m_points[i].pos.y);
m_projectedPoints[numPts++] = ProjectPoint(m_points[i].tanB.x, m_points[i].tanB.y);
}
}
}
void CCurveEditorCtrl::ClampToDomain(Vec2& rVec)
{
if (rVec.x < m_domainMinX)
{
rVec.x = m_domainMinX;
}
else if (rVec.x > m_domainMaxX)
{
rVec.x = m_domainMaxX;
}
if (rVec.y < m_domainMinY)
{
rVec.y = m_domainMinY;
}
else if (rVec.y > m_domainMaxY)
{
rVec.y = m_domainMaxY;
}
}
void CCurveEditorCtrl::GenerateDefaultCurve()
{
m_points.clear();
m_domainMinX = 0.0f;
m_domainMinY = 0.0f;
m_domainMaxX = 1.0f;
m_domainMaxY = 1.0f;
m_points.push_back(CurvePoint(0.00f, 0.00f));
m_points.push_back(CurvePoint(0.25f, 0.25f));
m_points.push_back(CurvePoint(0.50f, 0.50f));
m_points.push_back(CurvePoint(0.75f, 0.75f));
m_points.push_back(CurvePoint(1.00f, 1.00f));
}
void CCurveEditorCtrl::mousePressEvent(QMouseEvent* event)
{
QWidget::mousePressEvent(event);
if (event->button() != Qt::LeftButton)
{
return;
}
const QPoint point = event->pos();
if (m_bAllowMouse)
{
bool bSimpleSelect = !(event->modifiers() & Qt::ShiftModifier) && !(event->modifiers() & Qt::ControlModifier);
if (bSimpleSelect)
{
m_selectedIndices.clear();
}
for (size_t i = 0; i < m_points.size(); ++i)
{
QPoint ptProj = ProjectPoint(m_points[i].pos.x, m_points[i].pos.y);
QRect rcHandle(0, 0, CurveEditor::kHandleSize, CurveEditor::kHandleSize);
rcHandle.moveCenter(ptProj);
if (rcHandle.contains(point))
{
if (bSimpleSelect)
{
m_selectedIndices.push_back(i);
break;
}
if (event->modifiers() & Qt::ShiftModifier)
{
m_selectedIndices.push_back(i);
}
else if (event->modifiers() & Qt::ControlModifier)
{
std::vector<int>::iterator iter =
std::find(m_selectedIndices.begin(), m_selectedIndices.end(), i);
if (iter == m_selectedIndices.end())
{
m_selectedIndices.push_back(i);
}
else
{
m_selectedIndices.erase(iter);
}
}
}
}
m_bMouseDown = true;
m_lastMousePoint = point;
}
grabMouse();
update();
}
void CCurveEditorCtrl::mouseReleaseEvent(QMouseEvent* event)
{
QWidget::mouseReleaseEvent(event);
if (event->button() != Qt::LeftButton)
{
return;
}
m_bMouseDown = false;
m_bDragging = false;
m_selectedIndices.clear();
releaseMouse();
update();
}
void CCurveEditorCtrl::mouseMoveEvent(QMouseEvent* event)
{
if (m_bMouseDown && !m_bDragging)
{
m_bDragging = true;
}
m_bHovered = true;
if (m_flags & eFlag_ShowCursorAlways)
{
m_bHovered = true;
}
else
{
m_bHovered = false;
for (size_t i = 0; i < m_points.size(); ++i)
{
QPoint ptProj = ProjectPoint(m_points[i].pos.x, m_points[i].pos.y);
QRect rcHandle(0, 0, CurveEditor::kHandleSize, CurveEditor::kHandleSize);
rcHandle.moveCenter(ptProj);
if (rcHandle.contains(event->pos()))
{
m_bHovered = true;
break;
}
}
}
if (m_bDragging)
{
Vec2 v1 = UnprojectPoint(m_lastMousePoint);
Vec2 v2 = UnprojectPoint(event->pos());
Vec2 v = v1 - v2;
for (size_t i = 0; i < m_selectedIndices.size(); ++i)
{
int index = m_selectedIndices[i];
CurvePoint& cpt = m_points[index];
// do not move first and last points on X
if (index > 0 && index < m_points.size() - 1)
{
cpt.pos.x -= v.x;
}
cpt.pos.y -= v.y;
// lets check if the point is overlapping its neighbours
if (index > 0 && (index - 1) > 0)
{
if (cpt.pos.x < m_points[index - 1].pos.x)
{
CurvePoint p = m_points[index];
// swap!
m_points[index] = m_points[index - 1];
m_points[index - 1] = p;
m_selectedIndices[i] = index - 1;
}
}
if (index < m_points.size() - 1 && (index + 1) < m_points.size() - 1)
{
if (cpt.pos.x > m_points[index + 1].pos.x)
{
CurvePoint p = m_points[index];
// swap!
m_points[index] = m_points[index + 1];
m_points[index + 1] = p;
m_selectedIndices[i] = index + 1;
}
}
ClampToDomain(cpt.pos);
}
update();
m_lastMousePoint = event->pos();
}
QWidget::mouseMoveEvent(event);
}
void CCurveEditorCtrl::MarkX(float value)
{
m_marksX.push_back(value);
}
void CCurveEditorCtrl::MarkY(float value)
{
m_marksY.push_back(value);
}
@@ -0,0 +1,112 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_CURVEEDITORCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_CURVEEDITORCTRL_H
#pragma once
#include "Util/GdiUtil.h"
#include <QWidget>
#include <QPen>
class CCurveEditorCtrl
: public QWidget
{
public:
enum EFlags
{
eFlag_ShowVerticalRuler = (1 << 0),
eFlag_ShowHorizontalRuler = (1 << 1),
eFlag_ShowVerticalRulerText = (1 << 2),
eFlag_ShowHorizontalRulerText = (1 << 3),
eFlag_ShowPaddingBorder = (1 << 4),
eFlag_ShowMovingPointAxis = (1 << 5),
eFlag_ShowPointHandles = (1 << 6),
eFlag_ShowCursorAlways = (1 << 7),
eFlag_Disabled = (1 << 8) // special case, when disabling preview window.
};
CCurveEditorCtrl(QWidget* parent);
virtual ~CCurveEditorCtrl();
void SetFlags(UINT aFlags);
UINT GetFlags() const;
void SetMouseEnable(bool bEnable = true) { m_bAllowMouse = bEnable; }
bool GetMouseEnable() const {return m_bAllowMouse; }
bool SetDomainBounds(float aMinX, float aMinY, float aMaxX, float aMaxY);
void GetDomainBounds(float& rMinX, float& rMinY, float& rMaxX, float& rMaxY) const;
// labelsX/labelsY must be null (to use default labels)
// or contain aHorizontalSplits+1/aVerticalSplits+1 items.
void SetGrid(UINT aHorizontalSplits, UINT aVerticalSplits, const QStringList& labelsX = QStringList(), const QStringList& labelsY = QStringList());
void SetPadding(float padding) { m_padding = padding; }
void MarkX(float value);
void MarkY(float value);
void AddControlPoint(const Vec2& rPosition);
void ClearControlPoints();
void SetControlPointCount(UINT aCount);
UINT GetControlPointCount() const;
void SetControlPoint(UINT aIndex, const Vec2& rPosition);
void SetControlPointTangents(UINT aIndex, const Vec2& rLeft, const Vec2& rRight);
void GetControlPoint(UINT aIndex, Vec2& rOutPosition) const;
void GetControlPointTangents(UINT aIndex, Vec2& rOutLeft, Vec2& rOutRight) const;
QPoint ProjectPoint(float x, float y);
Vec2 UnprojectPoint(const QPoint& pt);
void UpdateProjectedPoints();
protected:
struct CurvePoint
{
CurvePoint(float aX = 0.0f, float aY = 0.0f)
{
pos.x = aX;
pos.y = aY;
}
Vec2 pos;
Vec2 tanA, tanB;
};
void ComputeTangents();
void ClampToDomain(Vec2& rVec);
void GenerateDefaultCurve();
void paintEvent(QPaintEvent* event) override;
std::vector<CurvePoint> m_points;
std::vector<QPoint> m_projectedPoints;
float m_domainMinX;
float m_domainMinY;
float m_domainMaxX;
float m_domainMaxY;
Vec2 m_gridSplits;
int m_padding;
bool m_bMouseDown, m_bDragging, m_bAllowMouse;
bool m_bHovered;
QPoint m_lastMousePoint;
std::vector<int> m_selectedIndices;
QFont m_fntInfo;
QPen m_pen, m_selCrossPen;
UINT m_flags;
QStringList m_labelsX;
QStringList m_labelsY;
std::vector<float> m_marksX;
std::vector<float> m_marksY;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_CURVEEDITORCTRL_H
@@ -0,0 +1,507 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "FolderTreeCtrl.h"
// Qt
#include <QMenu>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
// AzQtComponents
#include <AzQtComponents/Utilities/DesktopUtilities.h> // for AzQtComponents::ShowFileOnDesktop
enum ETreeImage
{
eTreeImage_Folder = 0,
eTreeImage_File = 2
};
enum CustomRoles
{
IsFolderRole = Qt::UserRole
};
//////////////////////////////////////////////////////////////////////////
// CFolderTreeCtrl
//////////////////////////////////////////////////////////////////////////
CFolderTreeCtrl::CFolderTreeCtrl(const QStringList& folders, const QString& fileNameSpec,
const QString& rootName, bool bDisableMonitor, bool bFlatTree, QWidget* parent)
: QTreeView(parent)
, m_rootTreeItem(nullptr)
, m_folders(folders)
, m_fileNameSpec(fileNameSpec)
, m_rootName(rootName)
, m_bDisableMonitor(bDisableMonitor)
, m_bFlatStyle(bFlatTree)
{
init(folders, fileNameSpec, rootName, bDisableMonitor, bFlatTree);
}
CFolderTreeCtrl::CFolderTreeCtrl(QWidget* parent)
: QTreeView(parent)
, m_rootTreeItem(nullptr)
, m_bDisableMonitor(false)
, m_bFlatStyle(true)
{
}
void CFolderTreeCtrl::init(const QStringList& folders, const QString& fileNameSpec, const QString& rootName, bool bDisableMonitor /*= false*/, bool bFlatTree /*= true*/)
{
m_model = new QStandardItemModel(this);
m_proxyModel = new QSortFilterProxyModel(this);
m_proxyModel->setRecursiveFilteringEnabled(true);
m_proxyModel->setSourceModel(m_model);
setModel(m_proxyModel);
m_folders = folders;
m_fileNameSpec = fileNameSpec;
m_rootName = rootName;
m_bDisableMonitor = bDisableMonitor;
m_bFlatStyle = bFlatTree;
m_fileIcon = QIcon(":/TreeView/default-icon.svg");
m_folderIcon = QIcon(":/TreeView/folder-icon.svg");
for (auto item = m_folders.begin(), end = m_folders.end(); item != end; ++item)
{
(*item) = Path::RemoveBackslash(Path::ToUnixPath((*item)));
if (CFileUtil::PathExists(*item))
{
m_foldersSegments.insert(std::make_pair((*item), Path::SplitIntoSegments((*item)).size()));
}
else if (Path::IsFolder((*item).toLocal8Bit().constData()))
{
m_foldersSegments.insert(std::make_pair((*item), Path::SplitIntoSegments((*item)).size()));
}
else
{
(*item).clear();
}
}
setHeaderHidden(true);
QObject::connect(this, &QTreeView::doubleClicked, this, &CFolderTreeCtrl::OnIndexDoubleClicked);
InitTree();
setSortingEnabled(true);
}
QString CFolderTreeCtrl::GetPath(QStandardItem* item) const
{
CTreeItem* treeItem = static_cast<CTreeItem*>(item);
if (treeItem)
{
return treeItem->GetPath();
}
return "";
}
bool CFolderTreeCtrl::IsFolder(QStandardItem* item) const
{
return item->data(IsFolderRole).toBool();
}
bool CFolderTreeCtrl::IsFile(QStandardItem* item) const
{
return !IsFolder(item);
}
CFolderTreeCtrl::~CFolderTreeCtrl()
{
// Obliterate tree items before destroying the controls
m_rootTreeItem.reset(nullptr);
// Unsubscribe from file change notifications
if (!m_bDisableMonitor)
{
CFileChangeMonitor::Instance()->Unsubscribe(this);
}
}
void CFolderTreeCtrl::OnIndexDoubleClicked(const QModelIndex& index)
{
if (!m_proxyModel || !m_model)
{
return;
}
QStandardItem* item = GetSourceItemByIndex(index);
if (item)
{
Q_EMIT ItemDoubleClicked(item);
}
}
void CFolderTreeCtrl::OnFileMonitorChange(const SFileChangeInfo& rChange)
{
const QString filePath = Path::ToUnixPath(Path::GetRelativePath(rChange.filename));
for (auto item = m_folders.begin(), end = m_folders.end(); item != end; ++item)
{
// Only look for changes in folder
if (filePath.indexOf((*item)) != 0)
{
return;
}
if (rChange.changeType == rChange.eChangeType_Created || rChange.changeType == rChange.eChangeType_RenamedNewName)
{
if (CFileUtil::PathExists(filePath))
{
LoadTreeRec(filePath);
}
else
{
AddItem(filePath);
}
}
else if (rChange.changeType == rChange.eChangeType_Deleted || rChange.changeType == rChange.eChangeType_RenamedOldName)
{
RemoveItem(filePath);
}
}
}
void CFolderTreeCtrl::contextMenuEvent(QContextMenuEvent* e)
{
if (!m_model)
{
return;
}
auto index = indexAt(e->pos());
QStandardItem* item = GetSourceItemByIndex(index);
if (!item)
{
return;
}
const QString path = GetPath(item);
QMenu menu;
QAction* editAction = menu.addAction(tr("Edit"));
connect(editAction, &QAction::triggered, this, [=]()
{
this->Edit(path);
});
QAction* showInExplorerAction = menu.addAction(tr("Show In Explorer"));
connect(showInExplorerAction, &QAction::triggered, this, [=]()
{
this->ShowInExplorer(path);
});
menu.exec(QCursor::pos());
}
void CFolderTreeCtrl::InitTree()
{
m_rootTreeItem.reset(new CTreeItem(*this, m_rootName));
for (auto item = m_folders.begin(), end = m_folders.end(); item != end; ++item)
{
if (!(*item).isEmpty())
{
LoadTreeRec((*item));
}
}
if (!m_bDisableMonitor)
{
CFileChangeMonitor::Instance()->Subscribe(this);
}
expandAll();
}
void CFolderTreeCtrl::LoadTreeRec(const QString& currentFolder)
{
CFileEnum fileEnum;
QFileInfo fileData;
QString currentFolderSlash = Path::AddSlash(currentFolder);
QString targetFolder = currentFolder;
if (currentFolder.startsWith('@'))
{
char resolvedPath[AZ_MAX_PATH_LEN] = { 0 };
if (AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(currentFolder.toLocal8Bit().constData(), resolvedPath, AZ_MAX_PATH_LEN))
{
targetFolder = resolvedPath;
}
// update the base folder name
QStringList parts = Path::SplitIntoSegments(currentFolderSlash);
if (parts.size() > 1)
{
parts.removeFirst();
currentFolderSlash = Path::AddSlash(parts.join(QDir::separator()));
}
}
for (bool bFoundFile = fileEnum.StartEnumeration(targetFolder, "*", &fileData);
bFoundFile; bFoundFile = fileEnum.GetNextFile(&fileData))
{
const QString fileName = fileData.fileName();
// Have we found a folder?
if (fileData.isDir())
{
// Skip the parent folder entries
if (fileName == "." || fileName == "..")
{
continue;
}
LoadTreeRec(currentFolderSlash + fileName);
}
AddItem(currentFolderSlash + fileName);
}
}
void CFolderTreeCtrl::AddItem(const QString& path)
{
QString folder;
QString fileNameWithoutExtension;
QString ext;
Path::Split(path, folder, fileNameWithoutExtension, ext);
auto regex = QRegExp(m_fileNameSpec, Qt::CaseInsensitive, QRegExp::Wildcard);
if (regex.exactMatch(path))
{
CTreeItem* folderTreeItem = CreateFolderItems(folder);
folderTreeItem->AddChild(fileNameWithoutExtension, path, eTreeImage_File);
}
}
void CFolderTreeCtrl::RemoveItem(const QString& path)
{
if (!CFileUtil::FileExists(path))
{
auto findIter = m_pathToTreeItem.find(path);
if (findIter != m_pathToTreeItem.end())
{
CTreeItem* foundItem = findIter->second;
foundItem->Remove();
RemoveEmptyFolderItems(Path::GetPath(path));
}
}
}
CFolderTreeCtrl::CTreeItem* CFolderTreeCtrl::GetItem(const QString& path)
{
auto findIter = m_pathToTreeItem.find(path);
if (findIter == m_pathToTreeItem.end())
{
return nullptr;
}
return findIter->second;
}
QStandardItem* CFolderTreeCtrl::GetSourceItemByIndex(const QModelIndex& index) const
{
if (!m_proxyModel || !m_model)
{
return nullptr;
}
// Since our tree view has a proxy model to handle the sorting/filtering, any index
// found on the tree view (e.g. the selected index) needs to be mapped back to the source
// model to find the actual item.
auto sourceIndex = m_proxyModel->mapToSource(index);
return m_model->itemFromIndex(sourceIndex);
}
QString CFolderTreeCtrl::CalculateFolderFullPath(const QStringList& splittedFolder, int idx)
{
QString path;
for (int segIdx = 0; segIdx <= idx; ++segIdx)
{
if (segIdx != 0)
{
path.append(QLatin1Char('/'));
}
path.append(splittedFolder[segIdx]);
}
return path;
}
CFolderTreeCtrl::CTreeItem* CFolderTreeCtrl::CreateFolderItems(const QString& folder)
{
QStringList splittedFolder = Path::SplitIntoSegments(folder);
CTreeItem* currentTreeItem = m_rootTreeItem.get();
if (!m_bFlatStyle)
{
QString currentFolder;
QString fullpath;
const int splittedFoldersCount = splittedFolder.size();
for (int idx = 0; idx < splittedFoldersCount; ++idx)
{
currentFolder = Path::RemoveBackslash(splittedFolder[idx]);
fullpath = CalculateFolderFullPath(splittedFolder, idx);
CTreeItem* folderItem = GetItem(fullpath);
if (!folderItem)
{
currentTreeItem = currentTreeItem->AddChild(currentFolder, fullpath, eTreeImage_Folder);
}
else
{
currentTreeItem = folderItem;
}
}
}
return currentTreeItem;
}
void CFolderTreeCtrl::RemoveEmptyFolderItems(const QString& folder)
{
QStringList splittedFolder = Path::SplitIntoSegments(folder);
const int splittedFoldersCount = splittedFolder.size();
QString fullpath;
for (int idx = 0; idx < splittedFoldersCount; ++idx)
{
fullpath = CalculateFolderFullPath(splittedFolder, idx);
CTreeItem* folderItem = GetItem(fullpath);
if (!folderItem)
{
continue;
}
if (!folderItem->hasChildren())
{
folderItem->Remove();
}
}
}
void CFolderTreeCtrl::Edit(const QString& path)
{
CFileUtil::EditTextFile(QtUtil::ToString(path), 0, IFileUtil::FILE_TYPE_SCRIPT);
}
void CFolderTreeCtrl::ShowInExplorer(const QString& path)
{
QString absolutePath = QDir::currentPath();
CTreeItem* root = m_rootTreeItem.get();
CTreeItem* item = GetItem(path);
if (item != root)
{
absolutePath += QStringLiteral("/%1").arg(path);
}
AzQtComponents::ShowFileOnDesktop(absolutePath);
}
QIcon CFolderTreeCtrl::GetItemIcon(int image) const
{
return image == eTreeImage_File ? m_fileIcon : m_folderIcon;
}
QList<QStandardItem*> CFolderTreeCtrl::GetSelectedItems() const
{
QList<QStandardItem*> items;
for (auto index : selectedIndexes())
{
QStandardItem* item = GetSourceItemByIndex(index);
if (item)
{
items.append(item);
}
}
return items;
}
void CFolderTreeCtrl::SetSearchFilter(const QString& searchText)
{
if (m_proxyModel)
{
m_proxyModel->setFilterFixedString(searchText);
}
}
//////////////////////////////////////////////////////////////////////////
// CFolderTreeCtrl::CTreeItem
//////////////////////////////////////////////////////////////////////////
CFolderTreeCtrl::CTreeItem::CTreeItem(CFolderTreeCtrl& folderTreeCtrl, const QString& path)
: QStandardItem(folderTreeCtrl.GetItemIcon(eTreeImage_Folder), folderTreeCtrl.m_rootName)
, m_folderTreeCtrl(folderTreeCtrl)
, m_path(path)
{
setData(true, IsFolderRole);
m_folderTreeCtrl.m_model->invisibleRootItem()->appendRow(this);
m_folderTreeCtrl.m_pathToTreeItem[ m_path ] = this;
}
CFolderTreeCtrl::CTreeItem::CTreeItem(CFolderTreeCtrl& folderTreeCtrl, CFolderTreeCtrl::CTreeItem* parent,
const QString& name, const QString& path, const int image)
: QStandardItem(folderTreeCtrl.GetItemIcon(image), name)
, m_folderTreeCtrl(folderTreeCtrl)
, m_path(path)
{
parent->appendRow(this);
setData(image == eTreeImage_Folder, IsFolderRole);
m_folderTreeCtrl.m_pathToTreeItem[ m_path ] = this;
}
CFolderTreeCtrl::CTreeItem::~CTreeItem()
{
m_folderTreeCtrl.m_pathToTreeItem.erase(m_path);
}
void CFolderTreeCtrl::CTreeItem::Remove()
{
// Root can't be deleted this way
if (auto parentItem = parent())
{
int numRows = parentItem->rowCount();
for (int i = 0; i < numRows; ++i)
{
if (parentItem->child(i) == this)
{
parentItem->removeRow(i);
break;
}
}
}
}
CFolderTreeCtrl::CTreeItem* CFolderTreeCtrl::CTreeItem::AddChild(const QString& name, const QString& path, const int image)
{
CTreeItem* newItem = new CTreeItem(m_folderTreeCtrl, this, name, path, image);
return newItem;
}
@@ -0,0 +1,124 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_FOLDERTREECTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_FOLDERTREECTRL_H
#pragma once
#include "Util/FileChangeMonitor.h"
#include <QList>
#include <QStandardItem>
#include <QTreeView>
#include <QIcon>
class QSortFilterProxyModel;
class QStandardItemModel;
//! Case insensetive less key for any type convertable to const char*.
struct qstring_icmp
{
bool operator()(const QString& left, const QString& right) const
{
return QString::compare(left, right, Qt::CaseInsensitive) < 0;
}
};
class CFolderTreeCtrl
: public QTreeView
, public CFileChangeMonitorListener
{
Q_OBJECT // AUTOMOC
friend class CTreeItem;
class CTreeItem
: public QStandardItem
{
// Only allow destruction through std::unique_ptr
friend struct std::default_delete<CTreeItem>;
public:
explicit CTreeItem(CFolderTreeCtrl& folderTreeCtrl, const QString& path);
explicit CTreeItem(CFolderTreeCtrl& folderTreeCtrl, CTreeItem* parent,
const QString& name, const QString& path, const int image);
void Remove();
CTreeItem* AddChild(const QString& name, const QString& path, const int image);
QString GetPath() const { return m_path; }
private:
~CTreeItem();
CFolderTreeCtrl& m_folderTreeCtrl;
QString m_path;
};
public:
CFolderTreeCtrl(QWidget* parent = 0);
CFolderTreeCtrl(const QStringList& folders, const QString& fileNameSpec,
const QString& rootName, bool bDisableMonitor = false, bool bFlatTree = true, QWidget* parent = 0);
virtual ~CFolderTreeCtrl();
void init(const QStringList& folders, const QString& fileNameSpec,
const QString& rootName, bool bDisableMonitor = false, bool bFlatTree = true);
QString GetPath(QStandardItem* item) const;
bool IsFolder(QStandardItem* item) const;
bool IsFile(QStandardItem* item) const;
QIcon GetItemIcon(int image) const;
QList<QStandardItem*> GetSelectedItems() const;
void SetSearchFilter(const QString& searchText);
Q_SIGNALS:
void ItemDoubleClicked(QStandardItem* item);
protected Q_SLOTS:
void OnIndexDoubleClicked(const QModelIndex& index);
protected:
virtual void OnFileMonitorChange(const SFileChangeInfo& rChange);
void contextMenuEvent(QContextMenuEvent* e) override;
void InitTree();
void LoadTreeRec(const QString& currentFolder);
void AddItem(const QString& path);
void RemoveItem(const QString& path);
CTreeItem* GetItem(const QString& path);
QStandardItem* GetSourceItemByIndex(const QModelIndex& index) const;
QString CalculateFolderFullPath(const QStringList& splittedFolder, int idx);
CTreeItem* CreateFolderItems(const QString& folder);
void RemoveEmptyFolderItems(const QString& folder);
void Edit(const QString& path);
void ShowInExplorer(const QString& path);
bool m_bDisableMonitor;
bool m_bFlatStyle;
std::unique_ptr< CTreeItem > m_rootTreeItem;
QString m_fileNameSpec;
QStringList m_folders;
QString m_rootName;
std::map<QString, unsigned int> m_foldersSegments;
QIcon m_folderIcon;
QIcon m_fileIcon;
QSortFilterProxyModel* m_proxyModel = nullptr;
QStandardItemModel* m_model = nullptr;
std::map< QString, CTreeItem*, qstring_icmp > m_pathToTreeItem;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_FOLDERTREECTRL_H
@@ -0,0 +1,52 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "HotTrackingTreeCtrl.h"
// Qt
#include <QMouseEvent>
CHotTrackingTreeCtrl::CHotTrackingTreeCtrl(QWidget* parent)
: QTreeWidget(parent)
{
setMouseTracking(true);
m_hHoverItem = NULL;
}
void CHotTrackingTreeCtrl::mouseMoveEvent(QMouseEvent* event)
{
QTreeWidgetItem* hItem = itemAt(event->pos());
if (m_hHoverItem != NULL)
{
QFont font = m_hHoverItem->font(0);
font.setBold(false);
m_hHoverItem->setFont(0, font);
m_hHoverItem = NULL;
}
if (hItem != NULL)
{
QFont font = hItem->font(0);
font.setBold(true);
hItem->setFont(0, font);
m_hHoverItem = hItem;
}
QTreeWidget::mouseMoveEvent(event);
}
#include <Controls/moc_HotTrackingTreeCtrl.cpp>
@@ -0,0 +1,37 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_HOTTRACKINGTREECTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_HOTTRACKINGTREECTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QTreeWidget>
#endif
class CHotTrackingTreeCtrl
: public QTreeWidget
{
Q_OBJECT
public:
CHotTrackingTreeCtrl(QWidget* parent = 0);
virtual ~CHotTrackingTreeCtrl(){};
protected:
void mouseMoveEvent(QMouseEvent* event) override;
private:
QTreeWidgetItem* m_hHoverItem;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_HOTTRACKINGTREECTRL_H
@@ -0,0 +1,451 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "ImageHistogramCtrl.h"
// Qt
#include <QPainter>
#include <QVBoxLayout>
#include <QLabel>
#include <QComboBox>
// CImageHistogramCtrl
// control tweak constants
namespace ImageHistogram
{
const float kGraphHeightPercent = 0.7f;
const int kGraphMargin = 4;
const QColor kBackColor = QColor(100, 100, 100);
const QColor kRedSectionColor = QColor(255, 220, 220);
const QColor kGreenSectionColor = QColor(220, 255, 220);
const QColor kBlueSectionColor = QColor(220, 220, 255);
const QColor kSplitSeparatorColor = QColor(100, 100, 0);
const QColor kButtonBackColor = QColor(20, 20, 20);
const QColor kBtnLightColor(200, 200, 200);
const QColor kBtnShadowColor(50, 50, 50);
const int kButtonWidth = 40;
const QColor kButtonTextColor(255, 255, 0);
const int kTextLeftSpacing = 4;
const int kTextFontSize = 70;
const char* kTextFontFace = "Arial";
const QColor kTextColor(255, 255, 255);
};
CImageHistogramCtrl::CImageHistogramCtrl(QWidget* parent)
: QWidget(parent)
, m_display(new CImageHistogramDisplay(this))
, m_drawMode(new QComboBox(this))
, m_infoText(new QLabel)
{
setWindowTitle("Image Histogram");
m_drawMode->setFocusPolicy(Qt::NoFocus);
m_drawMode->addItem(tr("Luminosity"),
QVariant::fromValue(EHistogramDrawMode::Luminosity));
m_drawMode->addItem(tr("Overlapped RGBA"),
QVariant::fromValue(EHistogramDrawMode::OverlappedRGB));
m_drawMode->addItem(tr("Split RGB"),
QVariant::fromValue(EHistogramDrawMode::SplitRGB));
m_drawMode->addItem(tr("Red Channel"),
QVariant::fromValue(EHistogramDrawMode::RedChannel));
m_drawMode->addItem(tr("Green Channel"),
QVariant::fromValue(EHistogramDrawMode::GreenChannel));
m_drawMode->addItem(tr("Blue Channel"),
QVariant::fromValue(EHistogramDrawMode::BlueChannel));
m_drawMode->addItem(tr("Alpha Channel"),
QVariant::fromValue(EHistogramDrawMode::AlphaChannel));
connect(m_drawMode, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[=]([[maybe_unused]] int index){
auto mode = drawMode();
m_display->m_drawMode = mode;
m_display->update();
});
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(m_drawMode);
layout->addWidget(m_display);
setLayout(layout);
setMinimumSize(200,150);
}
CImageHistogramCtrl::~CImageHistogramCtrl()
{
}
EHistogramDrawMode CImageHistogramCtrl::drawMode() const
{
return m_drawMode->currentData().value<EHistogramDrawMode>();
}
void CImageHistogramCtrl::setDrawMode(EHistogramDrawMode mode)
{
auto index = m_drawMode->findData(QVariant::fromValue(mode));
if (index != -1)
{
m_drawMode->setCurrentIndex(index);
}
}
void CImageHistogramCtrl::ComputeHistogram(CImageEx& image, CImageHistogram::EImageFormat format)
{
m_display->ComputeHistogram((BYTE*)image.GetData(), image.GetWidth(), image.GetHeight(), format);
}
CImageHistogramDisplay::CImageHistogramDisplay(QWidget* parent)
: QWidget(parent)
{
m_graphMargin = ImageHistogram::kGraphMargin;
m_drawMode = EHistogramDrawMode::Luminosity;
m_backColor = ImageHistogram::kBackColor;
m_graphHeightPercent = ImageHistogram::kGraphHeightPercent;
}
CImageHistogramDisplay::~CImageHistogramDisplay()
{}
void CImageHistogramDisplay::paintEvent([[maybe_unused]] QPaintEvent* event)
{
QPainter painter(this);
QColor penColor;
QString str, mode;
QRect rc, rcGraph;
QPen penSpikes;
QFont fnt;
QPen redPen, greenPen, bluePen, alphaPen;
redPen = QColor(255, 0, 0);
greenPen = QColor(0, 255, 0);
bluePen = QColor(0, 0, 255);
alphaPen = QColor(120, 120, 120);
rc = rect();
if (rc.isEmpty())
{
return;
}
painter.fillRect(rc, m_backColor);
penColor = QColor(0, 0, 0);
switch (m_drawMode)
{
case EHistogramDrawMode::Luminosity:
mode = "Lum";
break;
case EHistogramDrawMode::OverlappedRGB:
mode = "Overlap";
break;
case EHistogramDrawMode::SplitRGB:
mode = "R|G|B";
break;
case EHistogramDrawMode::RedChannel:
mode = "Red";
penColor = QColor(255, 0, 0);
break;
case EHistogramDrawMode::GreenChannel:
mode = "Green";
penColor = QColor(0, 255, 0);
break;
case EHistogramDrawMode::BlueChannel:
mode = "Blue";
penColor = QColor(0, 0, 255);
break;
case EHistogramDrawMode::AlphaChannel:
mode = "Alpha";
penColor = QColor(120, 120, 120);
break;
}
penSpikes = penColor;
painter.setPen(Qt::black);
painter.setBrush(Qt::white);
rcGraph = QRect(QPoint(m_graphMargin, m_graphMargin), QPoint(abs(rc.width() - m_graphMargin), abs(rc.height() * m_graphHeightPercent)));
painter.drawRect(rcGraph);
painter.setPen(penSpikes);
int i = 0;
int graphWidth = rcGraph.width() != 0 ? abs(rcGraph.width()) : 1;
int graphHeight = abs(rcGraph.height() - 1);
int graphBottom = abs(rcGraph.top() + rcGraph.height());
if (m_drawMode != EHistogramDrawMode::SplitRGB &&
m_drawMode != EHistogramDrawMode::OverlappedRGB)
{
int crtX = 0;
for (size_t x = 0, xCount = abs(rcGraph.width()); x < xCount; ++x)
{
float scale = 0;
i = ((float)x / graphWidth) * (kNumColorLevels - 1);
i = CLAMP(i, 0, kNumColorLevels - 1);
switch (m_drawMode)
{
case EHistogramDrawMode::Luminosity:
{
if (m_maxLumCount)
{
scale = (float)m_lumCount[i] / m_maxLumCount;
}
break;
}
case EHistogramDrawMode::RedChannel:
{
if (m_maxCount[0])
{
scale = (float)m_count[0][i] / m_maxCount[0];
}
break;
}
case EHistogramDrawMode::GreenChannel:
{
if (m_maxCount[1])
{
scale = (float)m_count[1][i] / m_maxCount[1];
}
break;
}
case EHistogramDrawMode::BlueChannel:
{
if (m_maxCount[2])
{
scale = (float)m_count[2][i] / m_maxCount[2];
}
break;
}
case EHistogramDrawMode::AlphaChannel:
{
if (m_maxCount[3])
{
scale = (float)m_count[3][i] / m_maxCount[3];
}
break;
}
}
crtX = rcGraph.left() + x + 1;
painter.drawLine(crtX, graphBottom, crtX, graphBottom - scale * graphHeight);
}
}
else
if (m_drawMode == EHistogramDrawMode::OverlappedRGB)
{
int lastHeight[kNumChannels] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX };
int heightR, heightG, heightB, heightA;
float scaleR, scaleG, scaleB, scaleA;
UINT crtX, prevX = INT_MAX;
for (size_t x = 0, xCount = abs(rcGraph.width()); x < xCount; ++x)
{
i = ((float)x / graphWidth) * (kNumColorLevels - 1);
i = CLAMP(i, 0, kNumColorLevels - 1);
crtX = rcGraph.left() + x + 1;
scaleR = scaleG = scaleB = scaleA = 0;
if (m_maxCount[0])
{
scaleR = (float)m_count[0][i] / m_maxCount[0];
}
if (m_maxCount[1])
{
scaleG = (float)m_count[1][i] / m_maxCount[1];
}
if (m_maxCount[2])
{
scaleB = (float)m_count[2][i] / m_maxCount[2];
}
if (m_maxCount[3])
{
scaleA = (float)m_count[3][i] / m_maxCount[3];
}
heightR = graphBottom - scaleR * graphHeight;
heightG = graphBottom - scaleG * graphHeight;
heightB = graphBottom - scaleB * graphHeight;
heightA = graphBottom - scaleA * graphHeight;
if (lastHeight[0] == INT_MAX)
{
lastHeight[0] = heightR;
}
if (lastHeight[1] == INT_MAX)
{
lastHeight[1] = heightG;
}
if (lastHeight[2] == INT_MAX)
{
lastHeight[2] = heightB;
}
if (lastHeight[3] == INT_MAX)
{
lastHeight[3] = heightA;
}
if (prevX == INT_MAX)
{
prevX = crtX;
}
painter.setPen(redPen);
painter.drawLine(prevX, lastHeight[0], crtX, heightR);
painter.setPen(greenPen);
painter.drawLine(prevX, lastHeight[1], crtX, heightG);
painter.setPen(bluePen);
painter.drawLine(prevX, lastHeight[2], crtX, heightB);
painter.setPen(alphaPen);
painter.drawLine(prevX, lastHeight[3], crtX, heightA);
lastHeight[0] = heightR;
lastHeight[1] = heightG;
lastHeight[2] = heightB;
lastHeight[3] = heightA;
prevX = crtX;
}
}
else
if (m_drawMode == EHistogramDrawMode::SplitRGB)
{
const float aThird = 1.0f / 3.0f;
const int aThirdOfNumColorLevels = kNumColorLevels / 3;
const int aThirdOfWidth = rcGraph.width() / 3;
QPen pPen;
float scale = 0, pos = 0;
// draw 3 blocks so we can see channel spaces
painter.fillRect(QRect(QPoint(rcGraph.left() + 1, rcGraph.top() + 1), QSize(aThirdOfWidth, rcGraph.height() - 2)), ImageHistogram::kRedSectionColor);
painter.fillRect(QRect(QPoint(rcGraph.left() + 1 + aThirdOfWidth, rcGraph.top() + 1), QSize(aThirdOfWidth, rcGraph.height() - 2)), ImageHistogram::kGreenSectionColor);
painter.fillRect(QRect(QPoint(rcGraph.left() + 1 + aThirdOfWidth * 2, rcGraph.top() + 1), QSize(aThirdOfWidth, rcGraph.height() - 2)), ImageHistogram::kBlueSectionColor);
// 3 split RGB channel histograms
for (size_t x = 0, xCount = abs(rcGraph.width()); x < xCount; ++x)
{
pos = (float)x / graphWidth;
i = (float)((int)(pos * kNumColorLevels) % aThirdOfNumColorLevels) / aThirdOfNumColorLevels * kNumColorLevels;
i = CLAMP(i, 0, kNumColorLevels - 1);
scale = 0;
// R
if (pos < aThird)
{
if (m_maxCount[0])
{
scale = (float)m_count[0][i] / m_maxCount[0];
}
pPen = redPen;
}
// G
if (pos > aThird && pos < aThird * 2)
{
if (m_maxCount[1])
{
scale = (float) m_count[1][i] / m_maxCount[1];
}
pPen = greenPen;
}
// B
if (pos > aThird * 2)
{
if (m_maxCount[2])
{
scale = (float) m_count[2][i] / m_maxCount[2];
}
pPen = bluePen;
}
painter.setPen(pPen);
painter.drawLine(rcGraph.left() + x + 1, graphBottom, rcGraph.left() + x + 1, graphBottom - scale * graphHeight);
}
// then draw 3 lines so we separate the channels
QPen wallPen(ImageHistogram::kSplitSeparatorColor, 1, Qt::DotLine);
painter.setPen(wallPen);
painter.drawLine(rcGraph.left() + aThirdOfWidth, rcGraph.bottom(), rcGraph.left() + aThirdOfWidth, rcGraph.top());
painter.drawLine(rcGraph.left() + aThirdOfWidth * 2, rcGraph.bottom(), rcGraph.left() + aThirdOfWidth * 2, rcGraph.top());
}
QRect rcText;
rcText = QRect(QPoint(m_graphMargin, rcGraph.height() + m_graphMargin * 2),
QPoint(rc.width(), rc.height() - m_graphMargin));
float mean = 0, stdDev = 0, median = 0;
switch (m_drawMode)
{
case EHistogramDrawMode::Luminosity:
case EHistogramDrawMode::SplitRGB:
case EHistogramDrawMode::OverlappedRGB:
mean = m_meanAvg;
stdDev = m_stdDevAvg;
median = m_medianAvg;
break;
case EHistogramDrawMode::RedChannel:
mean = m_mean[0];
stdDev = m_stdDev[0];
median = m_median[0];
break;
case EHistogramDrawMode::GreenChannel:
mean = m_mean[1];
stdDev = m_stdDev[1];
median = m_median[1];
break;
case EHistogramDrawMode::BlueChannel:
mean = m_mean[2];
stdDev = m_stdDev[2];
median = m_median[2];
break;
case EHistogramDrawMode::AlphaChannel:
mean = m_mean[3];
stdDev = m_stdDev[3];
median = m_median[3];
break;
}
str = tr("Mean: %1 StdDev: %2 Median: %3").arg(mean, 0, 'f', 2).arg(stdDev, 0, 'f', 2).arg(median, 0, 'f', 2);
fnt = QFont(ImageHistogram::kTextFontFace, ImageHistogram::kTextFontSize / 10);
painter.setFont(fnt);
painter.setPen(ImageHistogram::kTextColor);
painter.drawText(rcText, Qt::AlignCenter | Qt::TextSingleLine, painter.fontMetrics().elidedText(str, Qt::ElideRight, rcText.width(), Qt::TextSingleLine));
}
CImageHistogramDisplay* CImageHistogramCtrl::histogramDisplay() const
{
return m_display;
}
#include <Controls/moc_ImageHistogramCtrl.cpp>
@@ -0,0 +1,84 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_IMAGEHISTOGRAMCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_IMAGEHISTOGRAMCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include "Util/Image.h"
#include "Util/ImageHistogram.h"
#include <QWidget>
#endif
class QComboBox;
class QLabel;
enum class EHistogramDrawMode
{
Luminosity,
OverlappedRGB,
SplitRGB,
RedChannel,
GreenChannel,
BlueChannel,
AlphaChannel
};
Q_ENUMS(EHistogramDrawMode)
Q_DECLARE_METATYPE(EHistogramDrawMode)
class SANDBOX_API CImageHistogramDisplay
: public QWidget
, public CImageHistogram
{
Q_OBJECT
public:
CImageHistogramDisplay(QWidget* parent = nullptr);
virtual ~CImageHistogramDisplay();
void paintEvent(QPaintEvent* event) override;
EHistogramDrawMode m_drawMode;
int m_graphMargin;
float m_graphHeightPercent;
QColor m_backColor;
};
class SANDBOX_API CImageHistogramCtrl
: public QWidget
{
Q_OBJECT
public:
CImageHistogramCtrl(QWidget* parent = nullptr);
virtual ~CImageHistogramCtrl();
EHistogramDrawMode drawMode() const;
void setDrawMode(EHistogramDrawMode drawMode);
void ComputeHistogram(CImageEx& img, CImageHistogram::EImageFormat format);
CImageHistogramDisplay* histogramDisplay() const;
private:
CImageHistogramDisplay* m_display;
QComboBox* m_drawMode;
QLabel* m_infoText;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_IMAGEHISTOGRAMCTRL_H
@@ -0,0 +1,571 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "ImageListCtrl.h"
// Qt
#include <QPainter>
#include <QScrollBar>
//////////////////////////////////////////////////////////////////////////
CImageListCtrl::CImageListCtrl(QWidget* parent)
: QAbstractItemView(parent)
, m_itemSize(60, 60)
, m_borderSize(4, 4)
, m_style(DefaultStyle)
{
setItemDelegate(new QImageListDelegate(this));
setAutoFillBackground(false);
QPalette p = palette();
p.setColor(QPalette::Highlight, QColor(255, 55, 50));
setPalette(p);
horizontalScrollBar()->setRange(0, 0);
verticalScrollBar()->setRange(0, 0);
}
//////////////////////////////////////////////////////////////////////////
CImageListCtrl::~CImageListCtrl()
{
}
//////////////////////////////////////////////////////////////////////////
CImageListCtrl::ListStyle CImageListCtrl::Style() const
{
return m_style;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::SetStyle(ListStyle style)
{
m_style = style;
scheduleDelayedItemsLayout();
}
//////////////////////////////////////////////////////////////////////////
const QSize& CImageListCtrl::ItemSize() const
{
return m_itemSize;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::SetItemSize(QSize size)
{
Q_ASSERT(size.isValid());
m_itemSize = size;
scheduleDelayedItemsLayout();
}
//////////////////////////////////////////////////////////////////////////
const QSize& CImageListCtrl::BorderSize() const
{
return m_borderSize;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::SetBorderSize(QSize size)
{
Q_ASSERT(size.isValid());
m_borderSize = size;
scheduleDelayedItemsLayout();
}
//////////////////////////////////////////////////////////////////////////
QModelIndexList CImageListCtrl::ItemsInRect(const QRect& rect) const
{
QModelIndexList list;
if (!model())
{
return list;
}
QHash<int, QRect>::const_iterator i;
QHash<int, QRect>::const_iterator c = m_geometry.cend();
for (i = m_geometry.cbegin(); i != c; ++i)
{
if (i.value().intersects(rect))
{
list << model()->index(i.key(), 0, rootIndex());
}
}
return list;
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::paintEvent(QPaintEvent* event)
{
QAbstractItemView::paintEvent(event);
if (!model())
{
return;
}
const int rowCount = model()->rowCount();
if (m_geometry.isEmpty() && rowCount)
{
updateGeometries();
}
QPainter painter(viewport());
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
painter.setBackground(palette().window());
painter.setFont(font());
QStyleOptionViewItem option;
option.palette = palette();
option.font = font();
option.fontMetrics = fontMetrics();
option.decorationAlignment = Qt::AlignCenter;
const QRect visibleRect(QPoint(horizontalOffset(), verticalOffset()), viewport()->contentsRect().size());
painter.translate(-horizontalOffset(), -verticalOffset());
for (int r = 0; r < rowCount; ++r)
{
const QModelIndex& index = model()->index(r, 0, rootIndex());
option.rect = m_geometry.value(r);
if (!option.rect.intersects(visibleRect))
{
continue;
}
option.state = QStyle::State_None;
if (selectionModel()->isSelected(index))
{
option.state |= QStyle::State_Selected;
}
if (currentIndex() == index)
{
option.state |= QStyle::State_HasFocus;
}
QAbstractItemDelegate* idt = itemDelegate(index);
idt->paint(&painter, option, index);
}
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::rowsInserted(const QModelIndex& parent, int start, int end)
{
QAbstractItemView::rowsInserted(parent, start, end);
if (isVisible())
{
scheduleDelayedItemsLayout();
}
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::updateGeometries()
{
ClearItemGeometries();
if (!model())
{
return;
}
const int rowCount = model()->rowCount();
const int nPageHorz = viewport()->width();
const int nPageVert = viewport()->height();
if (nPageHorz == 0 || nPageVert == 0 || rowCount <= 0)
{
return;
}
int x = m_borderSize.width();
int y = m_borderSize.height();
const int nItemWidth = m_itemSize.width() + m_borderSize.width();
if (m_style == HorizontalStyle)
{
for (int row = 0; row < rowCount; ++row)
{
m_geometry.insert(row, QRect(QPoint(x, y), m_itemSize));
x += nItemWidth;
}
horizontalScrollBar()->setPageStep(viewport()->width());
horizontalScrollBar()->setRange(0, x - viewport()->width());
}
else
{
const int nTextHeight = fontMetrics().height();
const int nItemHeight = m_itemSize.height() + m_borderSize.height() + nTextHeight;
int nNumOfHorzItems = nPageHorz / nItemWidth;
if (nNumOfHorzItems <= 0)
{
nNumOfHorzItems = 1;
}
for (int row = 0; row < rowCount; ++row)
{
m_geometry.insert(row, QRect(QPoint(x, y), m_itemSize));
if ((row + 1) % nNumOfHorzItems == 0)
{
y += nItemHeight;
x = m_borderSize.width();
}
else
{
x += nItemWidth;
}
}
verticalScrollBar()->setPageStep(viewport()->height());
verticalScrollBar()->setRange(0, (y + nItemHeight) - viewport()->height());
}
}
//////////////////////////////////////////////////////////////////////////
QModelIndex CImageListCtrl::indexAt(const QPoint& point) const
{
if (!model())
{
return QModelIndex();
}
const QPoint p = point +
QPoint(horizontalOffset(), verticalOffset());
QHash<int, QRect>::const_iterator i;
QHash<int, QRect>::const_iterator c = m_geometry.cend();
for (i = m_geometry.cbegin(); i != c; ++i)
{
if (i.value().contains(p))
{
return model()->index(i.key(), 0, rootIndex());
}
}
return QModelIndex();
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::scrollTo(const QModelIndex& index, ScrollHint hint)
{
if (!index.isValid())
{
return;
}
QRect rect = m_geometry.value(index.row());
switch (hint)
{
case EnsureVisible:
if (horizontalOffset() > rect.right())
{
horizontalScrollBar()->setValue(rect.left());
}
else if ((horizontalOffset() + viewport()->width()) < rect.left())
{
horizontalScrollBar()->setValue(rect.right() - viewport()->width());
}
if (verticalOffset() > rect.bottom())
{
verticalScrollBar()->setValue(rect.top());
}
else if ((verticalOffset() + viewport()->height()) < rect.top())
{
verticalScrollBar()->setValue(rect.bottom() - viewport()->height());
}
break;
case PositionAtTop:
horizontalScrollBar()->setValue(rect.left());
verticalScrollBar()->setValue(rect.top());
break;
case PositionAtBottom:
horizontalScrollBar()->setValue(rect.right() - viewport()->width());
verticalScrollBar()->setValue(rect.bottom() - viewport()->height());
break;
case PositionAtCenter:
horizontalScrollBar()->setValue(rect.center().x() - (viewport()->width() / 2));
verticalScrollBar()->setValue(rect.center().y() - (viewport()->height() / 2));
break;
}
}
//////////////////////////////////////////////////////////////////////////
QRect CImageListCtrl::visualRect(const QModelIndex& index) const
{
if (!index.isValid())
{
return QRect();
}
if (!m_geometry.contains(index.row()))
{
return QRect();
}
return m_geometry.value(index.row())
.translated(-horizontalOffset(), -verticalOffset());
}
//////////////////////////////////////////////////////////////////////////
QRect CImageListCtrl::ItemGeometry(const QModelIndex& index) const
{
Q_ASSERT(index.model() == model());
Q_ASSERT(m_geometry.contains(index.row()));
return m_geometry.value(index.row());
}
void CImageListCtrl::SetItemGeometry(const QModelIndex& index, const QRect& rect)
{
Q_ASSERT(index.model() == model());
m_geometry.insert(index.row(), rect);
update(rect);
}
void CImageListCtrl::ClearItemGeometries()
{
m_geometry.clear();
}
//////////////////////////////////////////////////////////////////////////
int CImageListCtrl::horizontalOffset() const
{
return horizontalScrollBar()->value();
}
//////////////////////////////////////////////////////////////////////////
int CImageListCtrl::verticalOffset() const
{
return verticalScrollBar()->value();
}
//////////////////////////////////////////////////////////////////////////
bool CImageListCtrl::isIndexHidden([[maybe_unused]] const QModelIndex& index) const
{
return false; /* not supported */
}
//////////////////////////////////////////////////////////////////////////
QModelIndex CImageListCtrl::moveCursor(CursorAction cursorAction, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
{
if (!model())
{
return QModelIndex();
}
const int rowCount = model()->rowCount();
if (0 == rowCount)
{
return QModelIndex();
}
switch (cursorAction)
{
case MoveHome:
return model()->index(0, 0, rootIndex());
case MoveEnd:
return model()->index(rowCount - 1, 0, rootIndex());
case MovePrevious:
{
QModelIndex current = currentIndex();
if (current.isValid())
{
return model()->index((current.row() - 1) % rowCount, 0, rootIndex());
}
} break;
case MoveNext:
{
QModelIndex current = currentIndex();
if (current.isValid())
{
return model()->index((current.row() + 1) % rowCount, 0, rootIndex());
}
} break;
case MoveUp:
case MoveDown:
case MoveLeft:
case MoveRight:
case MovePageUp:
case MovePageDown:
/* TODO */
break;
}
return QModelIndex();
}
//////////////////////////////////////////////////////////////////////////
void CImageListCtrl::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags)
{
if (!model())
{
return;
}
const QRect lrect =
rect.translated(horizontalOffset(), verticalOffset());
QHash<int, QRect>::const_iterator i;
QHash<int, QRect>::const_iterator c = m_geometry.cend();
for (i = m_geometry.cbegin(); i != c; ++i)
{
if (i.value().intersects(lrect))
{
selectionModel()->select(model()->index(i.key(), 0, rootIndex()), flags);
}
}
}
//////////////////////////////////////////////////////////////////////////
QRegion CImageListCtrl::visualRegionForSelection(const QItemSelection& selection) const
{
QRegion region;
foreach(const QModelIndex &index, selection.indexes())
{
region += visualRect(index);
}
return region;
}
//////////////////////////////////////////////////////////////////////////
QImageListDelegate::QImageListDelegate(QObject* parent)
: QAbstractItemDelegate(parent)
{
}
//////////////////////////////////////////////////////////////////////////
void QImageListDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option, const QModelIndex& index) const
{
painter->save();
painter->setFont(option.font);
if (option.rect.isValid())
{
painter->setClipRect(option.rect);
}
QRect innerRect = option.rect.adjusted(1, 1, -1, -1);
QRect textRect(innerRect.left(), innerRect.bottom() - option.fontMetrics.height(),
innerRect.width(), option.fontMetrics.height() + 1);
/* fill item background */
painter->fillRect(option.rect, option.palette.color(QPalette::Base));
/* draw image */
if (index.data(Qt::DecorationRole).isValid())
{
const QPixmap& p = index.data(Qt::DecorationRole).value<QPixmap>();
if (p.isNull() || p.size() == QSize(1, 1))
{
emit InvalidPixmapGenerated(index);
}
else
{
painter->drawPixmap(innerRect, p);
}
}
/* draw text */
const QColor trColor = option.palette.color(QPalette::Shadow);
painter->fillRect(textRect, (option.state & QStyle::State_Selected) ?
trColor.lighter() : trColor);
if (option.state & QStyle::State_Selected)
{
painter->setPen(QPen(option.palette.color(QPalette::HighlightedText)));
QFont f = painter->font();
f.setBold(true);
painter->setFont(f);
}
else
{
painter->setPen(QPen(option.palette.color(QPalette::Text)));
}
painter->drawText(textRect, index.data(Qt::DisplayRole).toString(),
QTextOption(option.decorationAlignment));
painter->setPen(QPen(option.palette.color(QPalette::Shadow)));
painter->drawRect(textRect);
/* draw border */
if (option.state & QStyle::State_Selected)
{
QPen pen(option.palette.color(QPalette::Highlight));
pen.setWidth(2);
painter->setPen(pen);
painter->drawRect(innerRect);
}
else
{
painter->setPen(QPen(option.palette.color(QPalette::Shadow)));
painter->drawRect(option.rect);
}
if (option.state & QStyle::State_HasFocus)
{
QPen pen(Qt::DotLine);
pen.setColor(option.palette.color(QPalette::AlternateBase));
painter->setPen(pen);
painter->drawRect(option.rect);
}
painter->restore();
}
//////////////////////////////////////////////////////////////////////////
QSize QImageListDelegate::sizeHint(const QStyleOptionViewItem& option,
[[maybe_unused]] const QModelIndex& index) const
{
return option.rect.size();
}
//////////////////////////////////////////////////////////////////////////
QVector<int> QImageListDelegate::paintingRoles() const
{
return QVector<int>() << Qt::DecorationRole << Qt::DisplayRole;
}
#include <Controls/moc_ImageListCtrl.cpp>
@@ -0,0 +1,101 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_IMAGELISTCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_IMAGELISTCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QAbstractItemView>
#include <QHash>
#endif
//////////////////////////////////////////////////////////////////////////
// Custom control to display list of images.
//////////////////////////////////////////////////////////////////////////
class CImageListCtrl
: public QAbstractItemView
{
Q_OBJECT
public:
enum ListStyle
{
DefaultStyle,
HorizontalStyle
};
public:
CImageListCtrl(QWidget* parent = nullptr);
~CImageListCtrl();
ListStyle Style() const;
void SetStyle(ListStyle style);
const QSize& ItemSize() const;
void SetItemSize(QSize size);
const QSize& BorderSize() const;
void SetBorderSize(QSize size);
// Get all items inside specified rectangle.
QModelIndexList ItemsInRect(const QRect& rect) const;
QModelIndex indexAt(const QPoint& point) const override;
void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible) override;
QRect visualRect(const QModelIndex& index) const override;
protected:
QRect ItemGeometry(const QModelIndex& index) const;
void SetItemGeometry(const QModelIndex& index, const QRect& rect);
void ClearItemGeometries();
int horizontalOffset() const override;
int verticalOffset() const override;
bool isIndexHidden(const QModelIndex& index) const override;
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags) override;
QRegion visualRegionForSelection(const QItemSelection& selection) const override;
void paintEvent(QPaintEvent* event) override;
void rowsInserted(const QModelIndex& parent, int start, int end) override;
void updateGeometries() override;
private:
QHash<int, QRect> m_geometry;
QSize m_itemSize;
QSize m_borderSize;
ListStyle m_style;
};
class QImageListDelegate
: public QAbstractItemDelegate
{
Q_OBJECT
signals:
void InvalidPixmapGenerated(const QModelIndex& index) const;
public:
QImageListDelegate(QObject* parent = nullptr);
void paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QVector<int> paintingRoles() const override;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_IMAGELISTCTRL_H
@@ -0,0 +1,71 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "MultiMonHelper.h"
// Qt
#include <QScreen>
////////////////////////////////////////////////////////////////////////////
void ClipOrCenterRectToMonitor(QRect *prc, const UINT flags)
{
const QScreen* currentScreen = nullptr;
QRect rc;
Q_ASSERT(prc);
const auto screens = qApp->screens();
for (auto screen : screens)
{
if (screen->geometry().contains(prc->center()))
{
currentScreen = screen;
break;
}
}
if (!currentScreen)
{
return;
}
const int w = prc->width();
const int h = prc->height();
if (flags & MONITOR_WORKAREA)
{
rc = currentScreen->availableGeometry();
}
else
{
rc = currentScreen->geometry();
}
// center or clip the passed rect to the monitor rect
if (flags & MONITOR_CENTER)
{
prc->setLeft(rc.left() + (rc.right() - rc.left() - w) / 2);
prc->setTop(rc.top() + (rc.bottom() - rc.top() - h) / 2);
prc->setRight(prc->left() + w);
prc->setBottom(prc->top() + h);
}
else
{
prc->setLeft(qMax(rc.left(), qMin(rc.right() - w, prc->left())));
prc->setTop(qMax(rc.top(), qMin(rc.bottom() - h, prc->top())));
prc->setRight(prc->left() + w);
prc->setBottom(prc->top() + h);
}
}
@@ -0,0 +1,48 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_MULTIMONHELPER_H
#define CRYINCLUDE_EDITOR_CONTROLS_MULTIMONHELPER_H
#pragma once
// Taken from: http://msdn.microsoft.com/en-us/library/dd162826(v=vs.85).aspx
#define MONITOR_CENTER 0x0001 // center rect to monitor
#define MONITOR_CLIP 0x0000 // clip rect to monitor
#define MONITOR_WORKAREA 0x0002 // use monitor work area
#define MONITOR_AREA 0x0000 // use monitor entire area
//
// ClipOrCenterRectToMonitor
//
// The most common problem apps have when running on a
// multimonitor system is that they "clip" or "pin" windows
// based on the SM_CXSCREEN and SM_CYSCREEN system metrics.
// Because of app compatibility reasons these system metrics
// return the size of the primary monitor.
//
// This shows how you use the multi-monitor functions
// to do the same thing.
//
// params:
// prc : pointer to QRect to modify
// flags : some combination of the MONITOR_* flags above
//
// example:
//
// ClipOrCenterRectToMonitor(&aRect, MONITOR_CLIP | MONITOR_WORKAREA);
//
// Takes parameter pointer to RECT "aRect" and flags MONITOR_CLIP | MONITOR_WORKAREA
// This will modify aRect without resizing it so that it remains within the on-screen boundaries.
void ClipOrCenterRectToMonitor(QRect *prc, const UINT flags);
#endif // CRYINCLUDE_EDITOR_CONTROLS_MULTIMONHELPER_H
+147
View File
@@ -0,0 +1,147 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "NumberCtrl.h"
QNumberCtrl::QNumberCtrl(QWidget* parent)
: QDoubleSpinBox(parent)
, m_bMouseDown(false)
, m_bDragged(false)
, m_bUndoEnabled(false)
, m_prevValue(0)
{
connect(this, &QAbstractSpinBox::editingFinished, this, &QNumberCtrl::onEditingFinished);
}
void QNumberCtrl::changeEvent(QEvent* event)
{
if (event->type() == QEvent::EnabledChange)
{
setButtonSymbols(isEnabled() ? UpDownArrows : NoButtons);
}
QDoubleSpinBox::changeEvent(event);
}
void QNumberCtrl::SetRange(double newMin, double newMax)
{
// Avoid setting this value if its close to the current value, because otherwise qt will pump events into the queue to redraw/etc.
if ( (!AZ::IsClose(this->minimum(), newMin, DBL_EPSILON)) || (!AZ::IsClose(this->maximum(), newMax, DBL_EPSILON)) )
{
setRange(newMin, newMax);
}
}
void QNumberCtrl::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
emit mousePressed();
m_bMouseDown = true;
m_bDragged = false;
m_mousePos = event->pos();
if (m_bUndoEnabled && !CUndo::IsRecording())
{
GetIEditor()->BeginUndo();
}
emit dragStarted();
grabMouse();
}
QDoubleSpinBox::mousePressEvent(event);
}
void QNumberCtrl::mouseReleaseEvent(QMouseEvent* event)
{
QDoubleSpinBox::mouseReleaseEvent(event);
if (event->button() == Qt::LeftButton)
{
m_bMouseDown = m_bDragged = false;
emit valueUpdated();
emit valueChanged();
if (m_bUndoEnabled && CUndo::IsRecording())
{
GetIEditor()->AcceptUndo(m_undoText);
}
emit dragFinished();
releaseMouse();
m_prevValue = value();
emit mouseReleased();
}
}
void QNumberCtrl::mouseMoveEvent(QMouseEvent* event)
{
QDoubleSpinBox::mousePressEvent(event);
if (m_bMouseDown)
{
m_bDragged = true;
int dy = event->pos().y() - m_mousePos.y();
setValue(value() - singleStep() * dy);
emit valueUpdated();
m_mousePos = event->pos();
}
}
void QNumberCtrl::EnableUndo(const QString& undoText)
{
m_undoText = undoText;
m_bUndoEnabled = true;
}
void QNumberCtrl::focusInEvent(QFocusEvent* event)
{
m_prevValue = value();
QDoubleSpinBox::focusInEvent(event);
}
void QNumberCtrl::onEditingFinished()
{
bool undo = m_bUndoEnabled && !CUndo::IsRecording() && m_prevValue != value();
if (undo)
{
GetIEditor()->BeginUndo();
}
emit valueUpdated();
emit valueChanged();
if (undo)
{
GetIEditor()->AcceptUndo(m_undoText);
}
m_prevValue = value();
}
#include <Controls/moc_NumberCtrl.cpp>
+68
View File
@@ -0,0 +1,68 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_NUMBERCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_NUMBERCTRL_H
#pragma once
// NumberCtrl.h : header file
//
#if !defined(Q_MOC_RUN)
#include <QDoubleSpinBox>
#endif
class QNumberCtrl
: public QDoubleSpinBox
{
Q_OBJECT
public:
QNumberCtrl(QWidget* parent = nullptr);
bool IsDragging() const { return m_bDragged; }
//! If called will enable undo with given text when control is modified.
void EnableUndo(const QString& undoText);
void SetRange(double newMin, double maxRange);
Q_SIGNALS:
void dragStarted();
void dragFinished();
void valueUpdated();
void valueChanged();
void mouseReleased();
void mousePressed();
protected:
void changeEvent(QEvent* event) override;
void focusInEvent(QFocusEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
private:
void onEditingFinished();
void onValueChanged(double d);
bool m_bMouseDown;
bool m_bDragged;
QPoint m_mousePos;
bool m_bUndoEnabled;
double m_prevValue;
QString m_undoText;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_NUMBERCTRL_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,204 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_PREVIEWMODELCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_PREVIEWMODELCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QString>
#include <QPoint>
#include <QWidget>
#include <IStatObj.h>
#include <Editor/Material/Material.h>
#endif
struct IRenderNode;
class CImageEx;
class CPreviewModelCtrl
: public QWidget
, public IEditorNotifyListener
{
Q_OBJECT
public:
explicit CPreviewModelCtrl(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
QSize minimumSizeHint() const override;
public:
void LoadFile(const QString& modelFile, bool changeCamera = true);
Vec3 GetSize() const { return m_size; };
QString GetLoadedFile() const { return m_loadedFile; }
void SetEntity(IRenderNode* entity);
void SetObject(IStatObj* pObject);
IStatObj* GetObject() { return m_pObj; }
void SetCameraLookAt(float fRadiusScale, const Vec3& dir = Vec3(0, 1, 0));
void SetCameraRadius(float fRadius);
CCamera& GetCamera();
void SetGrid(bool bEnable) { m_bGrid = bEnable; }
void SetAxis(bool bEnable, bool forParticleEditor = false) { m_bAxis = bEnable; m_bAxisParticleEditor = forParticleEditor; }
void SetRotation(bool bEnable);
void SetClearColor(const ColorF& color);
void SetBackgroundTexture(const QString& textureFilename);
void UseBackLight(bool bEnable);
bool UseBackLight() const { return m_bUseBacklight; }
void SetShowNormals(bool bShow) { m_bShowNormals = bShow; }
void SetShowPhysics(bool bShow) { m_bShowPhysics = bShow; }
void SetShowRenderInfo(bool bShow) { m_bShowRenderInfo = bShow; }
void EnableUpdate(bool bEnable);
bool IsUpdateEnabled() const { return m_bUpdate; }
void Update(bool bForceUpdate = false);
void ProcessKeys();
// this turns on and off aspect-ratio-maintaining. Use it when the widget is free to resize itself.
void SetAspectRatio(float newAspectRatio);
int heightForWidth(int w) const override;
bool hasHeightForWidth() const override;
void SetMaterial(CMaterial* pMaterial);
CMaterial* GetMaterial();
void GetImageOffscreen(CImageEx& image, const QSize& customSize = QSize(0, 0));
void GetCameraTM(Matrix34& cameraTM);
void SetCameraTM(const Matrix34& cameraTM);
// Place camera so that whole object fits on screen.
void FitToScreen();
// Get information about the preview model.
int GetFaceCount();
int GetVertexCount();
int GetMaxLod();
int GetMtlCount();
void SetShowObject(bool bShowObject) {m_bShowObject = bShowObject; }
bool GetShowObject() {return m_bShowObject; }
void SetAmbient(ColorF amb) { m_ambientColor = amb; }
void SetAmbientMultiplier(f32 multiplier) { m_ambientMultiplier = multiplier; }
typedef void (* CameraChangeCallback)(void* m_userData, CPreviewModelCtrl* m_currentCamera);
void SetCameraChangeCallback(CameraChangeCallback callback, void* userData) { m_cameraChangeCallback = callback, m_pCameraChangeUserData = userData; }
void EnableMaterialPrecaching(bool bPrecacheMaterial) { m_bPrecacheMaterial = bPrecacheMaterial; }
void EnableWireframeRendering(bool bDrawWireframe) { m_bDrawWireFrame = bDrawWireframe; }
public:
~CPreviewModelCtrl();
bool CreateContext();
void ReleaseObject();
void DeleteRenderContex();
protected:
void OnCreate();
void OnDestroy();
void OnLButtonDown(QPoint point);
void OnLButtonUp(QPoint point);
void OnMButtonDown(QPoint point);
void OnMButtonUp(QPoint point);
void OnRButtonUp(QPoint point);
void OnRButtonDown(QPoint point);
QPaintEngine* paintEngine() const override;
void showEvent(QShowEvent* event) override;
void paintEvent(QPaintEvent* event) override;
void timerEvent(QTimerEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
virtual void OnEditorNotifyEvent(EEditorNotifyEvent event);
protected:
virtual bool Render();
virtual void SetCamera(CCamera& cam);
virtual void RenderObject(_smart_ptr<IMaterial> pMaterial, SRenderingPassInfo& passInfo);
HWND m_hWnd;
CCamera m_camera;
float m_fov;
struct SPreviousContext;
std::vector<SPreviousContext> m_previousContexts;
void SetOrbitAngles(const Ang3& ang);
void DrawGrid();
void DrawBackground();
_smart_ptr<IMaterial> GetCurrentMaterial();
_smart_ptr<IStatObj> m_pObj;
IRenderer* m_pRenderer;
bool m_bContextCreated;
Vec3 m_size;
Vec3 m_pos;
int m_nTimer;
bool m_useAspectRatio = false;
float m_aspectRatio = 1.0f;
QString m_loadedFile;
std::vector<CDLight> m_lights;
AABB m_aabb;
Vec3 m_cameraTarget;
float m_cameraRadius;
Vec3 m_cameraAngles;
bool m_bInRotateMode;
bool m_bInMoveMode;
bool m_bInPanMode;
QPoint m_mousePosition;
QPoint m_previousMousePosition;
IRenderNode* m_pEntity;
bool m_bHaveAnythingToRender;
bool m_bGrid;
bool m_bAxis;
bool m_bAxisParticleEditor;
bool m_bUpdate;
bool m_bRotate;
float m_rotateAngle;
ColorF m_clearColor;
ColorF m_ambientColor;
f32 m_ambientMultiplier;
bool m_bUseBacklight;
bool m_bShowObject;
bool m_bPrecacheMaterial;
bool m_bDrawWireFrame;
bool m_bShowNormals;
bool m_bShowPhysics;
bool m_bShowRenderInfo;
int m_backgroundTextureId;
float m_tileX;
float m_tileY;
float m_tileSizeX;
float m_tileSizeY;
_smart_ptr<CMaterial> m_pCurrentMaterial;
CameraChangeCallback m_cameraChangeCallback;
void* m_pCameraChangeUserData;
protected:
void StorePreviousContext();
void SetCurrentContext();
void RestorePreviousContext();
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_PREVIEWMODELCTRL_H
@@ -0,0 +1,176 @@
/*
* 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 "EditorDefs.h"
#include "QBitmapPreviewDialog.h"
#include <Controls/ui_QBitmapPreviewDialog.h>
#include <QApplication>
#include <QDesktopWidget>
#include <QPainter>
#include <QScreen>
void QBitmapPreviewDialog::ImageData::setRgba8888(const void* buffer, const int& w, const int& h)
{
const unsigned long bytes = w * h * 4;
m_buffer.resize(bytes);
memcpy(m_buffer.data(), buffer, bytes);
m_image = QImage((uchar*)m_buffer.constData(), w, h, QImage::Format::Format_RGBA8888);
}
static void fillChecker(int w, int h, unsigned int* dst)
{
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
dst[y * w + x] = 0xFF000000 | (((x >> 2) + (y >> 2)) % 2 == 0 ? 0x007F7F7F : 0x00000000);
}
}
}
QBitmapPreviewDialog::QBitmapPreviewDialog(QWidget* parent)
: QWidget(parent)
, ui(new Ui::QBitmapTooltip)
{
ui->setupUi(this);
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_ShowWithoutActivating);
// Clear label text
ui->m_placeholderBitmap->setText("");
ui->m_placeholderHistogram->setText("");
ui->m_bitmapSize->setProperty("tableRow", "Odd");
ui->m_Mips->setProperty("tableRow", "Even");
ui->m_Mean->setProperty("tableRow", "Odd");
ui->m_StdDev->setProperty("tableRow", "Even");
ui->m_Median->setProperty("tableRow", "Odd");
ui->m_labelForBitmapSize->setProperty("tooltipLabel", "content");
ui->m_labelForMean->setProperty("tooltipLabel", "content");
ui->m_labelForMedian->setProperty("tooltipLabel", "content");
ui->m_labelForMips->setProperty("tooltipLabel", "content");
ui->m_labelForStdDev->setProperty("tooltipLabel", "content");
ui->m_vBitmapSize->setProperty("tooltipLabel", "content");
ui->m_vMean->setProperty("tooltipLabel", "content");
ui->m_vMedian->setProperty("tooltipLabel", "content");
ui->m_vMips->setProperty("tooltipLabel", "content");
ui->m_vStdDev->setProperty("tooltipLabel", "content");
// Initialize placeholder images
const int w = 64;
const int h = 64;
QByteArray buffer;
buffer.resize(w * h * 4);
unsigned int* dst = (unsigned int*)buffer.data();
fillChecker(w, h, dst);
m_checker.setRgba8888(buffer.constData(), w, h);
m_initialSize = window()->window()->geometry().size();
}
QBitmapPreviewDialog::~QBitmapPreviewDialog()
{
delete ui;
}
void QBitmapPreviewDialog::setImageRgba8888(const void* buffer, const int& w, const int& h, [[maybe_unused]] const QString& info)
{
m_imageMain.setRgba8888(buffer, w, h);
}
QRect QBitmapPreviewDialog::getHistogramArea()
{
return QRect(ui->m_placeholderHistogram->pos(), ui->m_placeholderHistogram->size());
}
void QBitmapPreviewDialog::setFullSize(const bool& fullSize)
{
if (fullSize)
{
QSize desktop = QApplication::screenAt(ui->m_placeholderBitmap->pos())->availableGeometry().size();
QSize image = m_imageMain.m_image.size();
QPoint location = mapToGlobal(ui->m_placeholderBitmap->pos());
QSize finalSize;
finalSize.setWidth((image.width() < (desktop.width() - location.x())) ? image.width() : (desktop.width() - location.x()));
finalSize.setHeight((image.height() < (desktop.height() - location.y())) ? image.height() : (desktop.height() - location.y()));
float scale = (finalSize.width() < finalSize.height()) ? finalSize.width() / float(m_imageMain.m_image.width()) : finalSize.height() / float(m_imageMain.m_image.height());
ui->m_placeholderBitmap->setFixedSize(scale * m_imageMain.m_image.size());
}
else
{
ui->m_placeholderBitmap->setFixedSize(256, 256);
}
adjustSize();
update();
}
void QBitmapPreviewDialog::paintEvent(QPaintEvent* e)
{
QWidget::paintEvent(e);
QRect rect(ui->m_placeholderBitmap->pos(), ui->m_placeholderBitmap->size());
drawImageData(rect, m_imageMain);
}
void QBitmapPreviewDialog::drawImageData(const QRect& rect, const ImageData& imgData)
{
// Draw the
QPainter p(this);
p.drawImage(rect.topLeft(), m_checker.m_image.scaled(rect.size()));
p.drawImage(rect.topLeft(), imgData.m_image.scaled(rect.size()));
// Draw border
QPen pen;
pen.setColor(QColor(0, 0, 0));
p.drawRect(rect.top(), rect.left(), rect.width() - 1, rect.height());
}
void QBitmapPreviewDialog::setSize(QString _value)
{
ui->m_vBitmapSize->setText(_value);
}
void QBitmapPreviewDialog::setMips(QString _value)
{
ui->m_vMips->setText(_value);
}
void QBitmapPreviewDialog::setMean(QString _value)
{
ui->m_vMean->setText(_value);
}
void QBitmapPreviewDialog::setMedian(QString _value)
{
ui->m_vMedian->setText(_value);
}
void QBitmapPreviewDialog::setStdDev(QString _value)
{
ui->m_vStdDev->setText(_value);
}
QSize QBitmapPreviewDialog::GetCurrentBitmapSize()
{
return ui->m_placeholderBitmap->size();
}
QSize QBitmapPreviewDialog::GetOriginalImageSize()
{
return m_imageMain.m_image.size();
}
#include <Controls/moc_QBitmapPreviewDialog.cpp>
@@ -0,0 +1,68 @@
/*
* 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.
*
*/
#ifndef QBITMAPPREVIEWDIALOG_H
#define QBITMAPPREVIEWDIALOG_H
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <QPixmap>
#include <QImage>
#endif
class QLabel;
namespace Ui {
class QBitmapTooltip;
}
class QBitmapPreviewDialog
: public QWidget
{
Q_OBJECT
struct ImageData
{
QByteArray m_buffer;
QImage m_image;
void setRgba8888(const void* buffer, const int& w, const int& h);
};
public:
explicit QBitmapPreviewDialog(QWidget* parent = 0);
virtual ~QBitmapPreviewDialog();
QSize GetCurrentBitmapSize();
QSize GetOriginalImageSize();
protected:
void setImageRgba8888(const void* buffer, const int& w, const int& h, const QString& info);
void setSize(QString _value);
void setMips(QString _value);
void setMean(QString _value);
void setMedian(QString _value);
void setStdDev(QString _value);
QRect getHistogramArea();
void setFullSize(const bool& fullSize);
void paintEvent(QPaintEvent* e) override;
private:
void drawImageData(const QRect& rect, const ImageData& imgData);
protected:
Ui::QBitmapTooltip* ui;
QSize m_initialSize;
ImageData m_checker;
ImageData m_imageMain;
};
#endif // QBITMAPPREVIEWDIALOG_H
@@ -0,0 +1,390 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QBitmapTooltip</class>
<widget class="QWidget" name="QBitmapTooltip">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>256</width>
<height>510</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>256</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>Form</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="QLabel" name="m_placeholderBitmap">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>256</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string>Bitmap Area</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_placeholderHistogram">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>128</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string>Histogram Area</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="m_bitmapSize" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>24</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</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="QLabel" name="m_labelForBitmapSize">
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_vBitmapSize">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Size Value</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="m_Mips" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>24</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_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="QLabel" name="m_labelForMips">
<property name="text">
<string>DXT5 Mips:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_vMips">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Size Value</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="m_Mean" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>24</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<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>
<widget class="QLabel" name="m_labelForMean">
<property name="text">
<string>Mean:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_vMean">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Size Value</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="m_StdDev" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>24</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_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="m_labelForStdDev">
<property name="text">
<string>StdDev:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_vStdDev">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Size Value</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="m_Median" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>24</height>
</size>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_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="m_labelForMedian">
<property name="text">
<string>Median:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_vMedian">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Size Value</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,534 @@
/*
* 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 "EditorDefs.h"
#include "QBitmapPreviewDialogImp.h"
// Cry
#include <ITexture.h>
// EditorCore
#include <Util/Image.h>
#include <Include/IImageUtil.h>
// QT
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...'
#include <QEvent>
#include <QKeyEvent>
#include <QPainter>
#include <QPainterPath>
#include <qmath.h>
AZ_POP_DISABLE_WARNING
#include <Controls/ui_QBitmapPreviewDialog.h>
static const int kDefaultWidth = 256;
static const int kDefaultHeight = 256;
QBitmapPreviewDialogImp::QBitmapPreviewDialogImp(QWidget* parent)
: QBitmapPreviewDialog(parent)
, m_image(new CImageEx())
, m_showOriginalSize(false)
, m_showMode(ESHOW_RGB)
, m_histrogramMode(eHistogramMode_OverlappedRGB)
{
setMouseTracking(true);
setImage("");
ui->m_placeholderBitmap->setStyleSheet("background-color: rgba(0, 0, 0, 0);");
ui->m_placeholderHistogram->setStyleSheet("background-color: rgba(0, 0, 0, 0);");
ui->m_labelForBitmapSize->setProperty("tooltipLabel", "Content");
ui->m_labelForMean->setProperty("tooltipLabel", "Content");
ui->m_labelForMedian->setProperty("tooltipLabel", "Content");
ui->m_labelForMips->setProperty("tooltipLabel", "Content");
ui->m_labelForStdDev->setProperty("tooltipLabel", "Content");
ui->m_vBitmapSize->setProperty("tooltipLabel", "Content");
ui->m_vMean->setProperty("tooltipLabel", "Content");
ui->m_vMedian->setProperty("tooltipLabel", "Content");
ui->m_vMips->setProperty("tooltipLabel", "Content");
ui->m_vStdDev->setProperty("tooltipLabel", "Content");
setUIStyleMode(EUISTYLE_IMAGE_ONLY);
}
QBitmapPreviewDialogImp::~QBitmapPreviewDialogImp()
{
SAFE_DELETE(m_image);
}
void QBitmapPreviewDialogImp::setImage(const QString path)
{
if (path.isEmpty()
|| m_path == path
|| !GetIEditor()->GetImageUtil()->LoadImage(path.toUtf8().data(), *m_image))
{
return;
}
m_showOriginalSize = isSizeSmallerThanDefault();
m_path = path;
refreshData();
}
void QBitmapPreviewDialogImp::setShowMode(EShowMode mode)
{
if (mode == ESHOW_NumModes)
{
return;
}
m_showMode = mode;
refreshData();
update();
}
void QBitmapPreviewDialogImp::toggleShowMode()
{
m_showMode = (EShowMode)(((int)m_showMode + 1) % ESHOW_NumModes);
refreshData();
update();
}
void QBitmapPreviewDialogImp::setUIStyleMode(EUIStyle mode)
{
if (mode >= EUISTYLE_NumModes)
{
return;
}
m_uiStyle = mode;
if (m_uiStyle == EUISTYLE_IMAGE_ONLY)
{
ui->m_placeholderHistogram->hide();
ui->m_labelForBitmapSize->hide();
ui->m_labelForMean->hide();
ui->m_labelForMedian->hide();
ui->m_labelForMips->hide();
ui->m_labelForStdDev->hide();
ui->m_vBitmapSize->hide();
ui->m_vMean->hide();
ui->m_vMedian->hide();
ui->m_vMips->hide();
ui->m_vStdDev->hide();
}
else
{
ui->m_placeholderHistogram->show();
ui->m_labelForBitmapSize->show();
ui->m_labelForMean->show();
ui->m_labelForMedian->show();
ui->m_labelForMips->show();
ui->m_labelForStdDev->show();
ui->m_vBitmapSize->show();
ui->m_vMean->show();
ui->m_vMedian->show();
ui->m_vMips->show();
ui->m_vStdDev->show();
}
}
const QBitmapPreviewDialogImp::EShowMode& QBitmapPreviewDialogImp::getShowMode() const
{
return m_showMode;
}
void QBitmapPreviewDialogImp::setHistogramMode(EHistogramMode mode)
{
if (mode == eHistogramMode_NumModes)
{
return;
}
m_histrogramMode = mode;
}
void QBitmapPreviewDialogImp::toggleHistrogramMode()
{
m_histrogramMode = (EHistogramMode)(((int)m_histrogramMode + 1) % eHistogramMode_NumModes);
update();
}
const QBitmapPreviewDialogImp::EHistogramMode& QBitmapPreviewDialogImp::getHistogramMode() const
{
return m_histrogramMode;
}
void QBitmapPreviewDialogImp::toggleOriginalSize()
{
m_showOriginalSize = !m_showOriginalSize;
refreshData();
update();
}
bool QBitmapPreviewDialogImp::isSizeSmallerThanDefault()
{
return m_image->GetWidth() < kDefaultWidth && m_image->GetHeight() < kDefaultHeight;
}
void QBitmapPreviewDialogImp::setOriginalSize(bool value)
{
m_showOriginalSize = value;
refreshData();
update();
}
const char* QBitmapPreviewDialogImp::GetShowModeDescription(EShowMode eShowMode, [[maybe_unused]] bool bShowInOriginalSize) const
{
switch (eShowMode)
{
case ESHOW_RGB:
return "RGB";
case ESHOW_RGB_ALPHA:
return "RGB+A";
case ESHOW_ALPHA:
return "Alpha";
case ESHOW_RGBA:
return "RGBA";
case ESHOW_RGBE:
return "RGBExp";
}
return "";
}
const char* getHistrogramModeStr(QBitmapPreviewDialogImp::EHistogramMode mode, bool shortName)
{
switch (mode)
{
case QBitmapPreviewDialogImp::eHistogramMode_Luminosity:
return shortName ? "Lum" : "Luminosity";
case QBitmapPreviewDialogImp::eHistogramMode_OverlappedRGB:
return shortName ? "Overlap" : "Overlapped RGBA";
case QBitmapPreviewDialogImp::eHistogramMode_SplitRGB:
return shortName ? "R|G|B" : "Split RGB";
case QBitmapPreviewDialogImp::eHistogramMode_RedChannel:
return shortName ? "Red" : "Red Channel";
case QBitmapPreviewDialogImp::eHistogramMode_GreenChannel:
return shortName ? "Green" : "Green Channel";
case QBitmapPreviewDialogImp::eHistogramMode_BlueChannel:
return shortName ? "Blue" : "Blue Channel";
case QBitmapPreviewDialogImp::eHistogramMode_AlphaChannel:
return shortName ? "Alpha" : "Alpha Channel";
default:
break;
}
return "";
}
void QBitmapPreviewDialogImp::refreshData()
{
// Check if we have some usefull data loaded
if (m_image->GetWidth() * m_image->GetHeight() == 0)
{
return;
}
int w = m_image->GetWidth();
int h = m_image->GetHeight();
bool hasAlpha = m_image->HasAlphaChannel();
bool isLimitedHDR = m_image->IsLimitedHDR();
int multiplier = (m_showMode == ESHOW_RGB_ALPHA ? 2 : 1);
int originalW = w * multiplier;
int originalH = h;
if (!m_showOriginalSize || (w == 0))
{
w = kDefaultWidth;
}
if (!m_showOriginalSize || (h == 0))
{
h = kDefaultHeight;
}
w *= multiplier;
CImageEx scaledImage;
if (m_showOriginalSize && (originalW < w))
{
w = originalW;
}
if (m_showOriginalSize && (originalH < h))
{
h = originalH;
}
scaledImage.Allocate(w, h);
if (m_showMode == ESHOW_RGB_ALPHA)
{
GetIEditor()->GetImageUtil()->ScaleToDoubleFit(*m_image, scaledImage);
}
else
{
GetIEditor()->GetImageUtil()->ScaleToFit(*m_image, scaledImage);
}
if (m_showMode == ESHOW_RGB || m_showMode == ESHOW_RGBE)
{
scaledImage.FillAlpha();
}
else if (m_showMode == ESHOW_ALPHA)
{
for (int h2 = 0; h2 < scaledImage.GetHeight(); h2++)
{
for (int w2 = 0; w2 < scaledImage.GetWidth(); w2++)
{
int a = scaledImage.ValueAt(w2, h2) >> 24;
scaledImage.ValueAt(w2, h2) = RGB(a, a, a) | (a << 24);
}
}
}
else if (m_showMode == ESHOW_RGB_ALPHA)
{
int halfWidth = scaledImage.GetWidth() / 2;
for (int h2 = 0; h2 < scaledImage.GetHeight(); h2++)
{
for (int w2 = 0; w2 < halfWidth; w2++)
{
int r = GetRValue(scaledImage.ValueAt(w2, h2));
int g = GetGValue(scaledImage.ValueAt(w2, h2));
int b = GetBValue(scaledImage.ValueAt(w2, h2));
int a = scaledImage.ValueAt(w2, h2) >> 24;
scaledImage.ValueAt(w2, h2) = RGB(r, g, b) | (a << 24);
scaledImage.ValueAt(w2 + halfWidth, h2) = RGB(a, a, a) | (a << 24);
}
}
}
setImageRgba8888(scaledImage.GetData(), w, h, "");
setSize(QString().asprintf("%d x %d", m_image->GetWidth(), m_image->GetHeight()));
setMips(QString().asprintf("%d", m_image->GetNumberOfMipMaps()));
setFullSize(m_showOriginalSize);
// Compute histogram
m_histogram.ComputeHistogram((BYTE*)scaledImage.GetData(), w, h, CImageHistogram::eImageFormat_32BPP_RGBA);
}
void QBitmapPreviewDialogImp::paintEvent(QPaintEvent* e)
{
QBitmapPreviewDialog::paintEvent(e);
//if showing original size hide other information so it's easier to see
if (m_showOriginalSize)
{
return;
}
if (m_uiStyle == EUISTYLE_IMAGE_ONLY)
{
return;
}
QPainter p(this);
QPen pen;
QPainterPath path[4];
// Fill background color
QRect histogramRect = getHistogramArea();
p.fillRect(histogramRect, QColor(255, 255, 255));
// Draw borders
pen.setColor(QColor(0, 0, 0));
p.setPen(pen);
p.drawRect(histogramRect);
// Draw histogram
QVector<int> drawChannels;
switch (m_histrogramMode)
{
case eHistogramMode_Luminosity:
drawChannels.push_back(3);
break;
case eHistogramMode_SplitRGB:
drawChannels.push_back(0);
drawChannels.push_back(1);
drawChannels.push_back(2);
break;
case eHistogramMode_OverlappedRGB:
drawChannels.push_back(0);
drawChannels.push_back(1);
drawChannels.push_back(2);
break;
case eHistogramMode_RedChannel:
drawChannels.push_back(0);
break;
case eHistogramMode_GreenChannel:
drawChannels.push_back(1);
break;
case eHistogramMode_BlueChannel:
drawChannels.push_back(2);
break;
case eHistogramMode_AlphaChannel:
drawChannels.push_back(3);
break;
}
int graphWidth = qMax(histogramRect.width(), 1);
int graphHeight = qMax(histogramRect.height() - 2, 0);
int graphBottom = histogramRect.bottom() + 1;
int currX[4] = {0, 0, 0, 0};
int prevX[4] = {0, 0, 0, 0};
float scale = 0.0f;
static const int numSubGraphs = 3;
const int subGraph = qCeil(graphWidth / numSubGraphs);
// Fill background for Split RGB histogram
if (m_histrogramMode == eHistogramMode_SplitRGB)
{
const static QColor backgroundColor[numSubGraphs] =
{
QColor(255, 220, 220),
QColor(220, 255, 220),
QColor(220, 220, 255)
};
for (int i = 0; i < numSubGraphs; i++)
{
p.fillRect(histogramRect.left() + subGraph * i,
histogramRect.top(),
subGraph + (i == numSubGraphs - 1 ? 1 : 0),
histogramRect.height(), backgroundColor[i]);
}
}
int lastHeight[CImageHistogram::kNumChannels] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX };
for (int x = 0; x < graphWidth; ++x)
{
for (int j = 0; j < drawChannels.size(); j++)
{
const int c = drawChannels[j];
int& curr_x = currX[c];
int& prev_x = prevX[c];
int& last_height = lastHeight[c];
QPainterPath& curr_path = path[c];
curr_x = histogramRect.left() + x + 1;
int i = ((float)x / (graphWidth - 1)) * (CImageHistogram::kNumColorLevels - 1);
if (m_histrogramMode == eHistogramMode_SplitRGB)
{
// Filter out to area which we are interested
const int k = x / subGraph;
if (k != c)
{
continue;
}
i = qCeil((i - (subGraph * c)) * numSubGraphs);
i = qMin(i, CImageHistogram::kNumColorLevels - 1);
i = qMax(i, 0);
}
if (m_histrogramMode == eHistogramMode_Luminosity)
{
scale = (float)m_histogram.m_lumCount[i] / m_histogram.m_maxLumCount;
}
else if (m_histogram.m_maxCount[c])
{
scale = (float)m_histogram.m_count[c][i] / m_histogram.m_maxCount[c];
}
int height = graphBottom - graphHeight * scale;
if (last_height == INT_MAX)
{
last_height = height;
}
curr_path.moveTo(prev_x, last_height);
curr_path.lineTo(curr_x, height);
last_height = height;
if (prev_x == INT_MAX)
{
prev_x = curr_x;
}
prev_x = curr_x;
}
}
static const QColor kChannelColor[4] =
{
QColor(255, 0, 0),
QColor(0, 255, 0),
QColor(0, 0, 255),
QColor(120, 120, 120)
};
for (int i = 0; i < drawChannels.size(); i++)
{
const int c = drawChannels[i];
pen.setColor(kChannelColor[c]);
p.setPen(pen);
p.drawPath(path[c]);
}
// Update histogram info
{
float mean = 0, stdDev = 0, median = 0;
switch (m_histrogramMode)
{
case eHistogramMode_Luminosity:
case eHistogramMode_SplitRGB:
case eHistogramMode_OverlappedRGB:
mean = m_histogram.m_meanAvg;
stdDev = m_histogram.m_stdDevAvg;
median = m_histogram.m_medianAvg;
break;
case eHistogramMode_RedChannel:
mean = m_histogram.m_mean[0];
stdDev = m_histogram.m_stdDev[0];
median = m_histogram.m_median[0];
break;
case eHistogramMode_GreenChannel:
mean = m_histogram.m_mean[1];
stdDev = m_histogram.m_stdDev[1];
median = m_histogram.m_median[1];
break;
case eHistogramMode_BlueChannel:
mean = m_histogram.m_mean[2];
stdDev = m_histogram.m_stdDev[2];
median = m_histogram.m_median[2];
break;
case eHistogramMode_AlphaChannel:
mean = m_histogram.m_mean[3];
stdDev = m_histogram.m_stdDev[3];
median = m_histogram.m_median[3];
break;
}
QString val;
val.setNum(mean);
setMean(val);
val.setNum(stdDev);
setStdDev(val);
val.setNum(median);
setMedian(val);
}
}
#include <Controls/moc_QBitmapPreviewDialogImp.cpp>
@@ -0,0 +1,93 @@
/*
* 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.
*
*/
#ifndef QBITMAPPREVIEWDIALOG_IMP_H
#define QBITMAPPREVIEWDIALOG_IMP_H
#if !defined(Q_MOC_RUN)
#include "QBitmapPreviewDialog.h"
#include <Util/ImageHistogram.h>
#endif
class CImageEx;
class QBitmapPreviewDialogImp
: public QBitmapPreviewDialog
{
Q_OBJECT;
public:
enum EUIStyle
{
EUISTYLE_IMAGE_ONLY,
EUISTYLE_IMAGE_HISTOGRAM,
EUISTYLE_NumModes
};
enum EShowMode
{
ESHOW_RGB = 0,
ESHOW_ALPHA,
ESHOW_RGBA,
ESHOW_RGB_ALPHA,
ESHOW_RGBE,
ESHOW_NumModes,
};
enum EHistogramMode
{
eHistogramMode_Luminosity,
eHistogramMode_OverlappedRGB,
eHistogramMode_SplitRGB,
eHistogramMode_RedChannel,
eHistogramMode_GreenChannel,
eHistogramMode_BlueChannel,
eHistogramMode_AlphaChannel,
eHistogramMode_NumModes,
};
explicit QBitmapPreviewDialogImp(QWidget* parent = 0);
virtual ~QBitmapPreviewDialogImp();
void setImage(const QString path);
void setShowMode(EShowMode mode);
void toggleShowMode();
void setUIStyleMode(EUIStyle mode);
const EShowMode& getShowMode() const;
void setHistogramMode(EHistogramMode mode);
void toggleHistrogramMode();
const EHistogramMode& getHistogramMode() const;
void setOriginalSize(bool value);
void toggleOriginalSize();
bool isSizeSmallerThanDefault();
void paintEvent(QPaintEvent* e) override;
protected:
void refreshData();
private:
const char* GetShowModeDescription(EShowMode eShowMode, bool bShowInOriginalSize) const;
private:
CImageEx* m_image;
QString m_path;
CImageHistogram m_histogram;
bool m_showOriginalSize;
EShowMode m_showMode;
EHistogramMode m_histrogramMode;
EUIStyle m_uiStyle;
};
#endif // QBITMAPPREVIEWDIALOG_IMP_H
@@ -0,0 +1,589 @@
/*
* 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 "EditorDefs.h"
#include "QRollupCtrl.h"
// Qt
#include <QMenu>
#include <QStylePainter>
#include <QVBoxLayout>
#include <QSettings>
#include <QToolButton>
#include <QStyleOptionToolButton>
//////////////////////////////////////////////////////////////////////////
class QRollupCtrlButton
: public QToolButton
{
public:
QRollupCtrlButton(QWidget* parent);
inline void setSelected(bool b) { selected = b; update(); }
inline bool isSelected() const { return selected; }
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
protected:
void paintEvent(QPaintEvent*) override;
private:
bool selected;
};
QRollupCtrlButton::QRollupCtrlButton(QWidget* parent)
: QToolButton(parent)
, selected(true)
{
setBackgroundRole(QPalette::Window);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
setFocusPolicy(Qt::NoFocus);
setStyleSheet("* {margin: 2px 5px 2px 5px; border: 1px solid #CBA457;}");
}
QSize QRollupCtrlButton::sizeHint() const
{
QSize iconSize(8, 8);
if (!icon().isNull())
{
int icone = style()->pixelMetric(QStyle::PM_SmallIconSize);
iconSize += QSize(icone + 2, icone);
}
QSize textSize = fontMetrics().size(Qt::TextShowMnemonic, text()) + QSize(0, 8);
QSize total(iconSize.width() + textSize.width(), qMax(iconSize.height(), textSize.height()));
return total.expandedTo(QApplication::globalStrut());
}
QSize QRollupCtrlButton::minimumSizeHint() const
{
if (icon().isNull())
{
return QSize();
}
int icone = style()->pixelMetric(QStyle::PM_SmallIconSize);
return QSize(icone + 8, icone + 8);
}
void QRollupCtrlButton::paintEvent(QPaintEvent*)
{
QStylePainter p(this);
// draw the background manually, not to clash with UI 2.0 style shets
// the numbers here are taken from the stylesheet in the constructor
p.fillRect(QRect(5, 1, width() - 10, height() - 3), QColor(52, 52, 52));
{
QStyleOptionToolButton opt;
initStyleOption(&opt);
if (isSelected())
{
if (opt.state & QStyle::State_MouseOver)
{
opt.state |= QStyle::State_Sunken;
}
opt.state |= QStyle::State_MouseOver;
}
p.drawComplexControl(QStyle::CC_ToolButton, opt);
}
{
p.setPen(QPen(QColor(132, 128, 125)));
int top = height() / 2 - 2;
p.drawLine(2, top, 4, top);
p.drawLine(width() - 5, top, width() - 3, top);
int bottom = !isSelected() ? top + 4 : height();
p.drawLine(2, bottom, 2, top);
p.drawLine(width() - 3, bottom, width() - 3, top);
if (!isSelected())
{
p.drawLine(2, bottom, 4, bottom);
p.drawLine(width() - 5, bottom, width() - 3, bottom);
}
}
}
//////////////////////////////////////////////////////////////////////////
QRollupCtrl::Page* QRollupCtrl::page(QWidget* widget) const
{
if (!widget)
{
return 0;
}
for (PageList::ConstIterator i = m_pageList.constBegin(); i != m_pageList.constEnd(); ++i)
{
if ((*i).widget == widget)
{
return (Page*)&(*i);
}
}
return 0;
}
QRollupCtrl::Page* QRollupCtrl::page(int index)
{
if (index >= 0 && index < m_pageList.size())
{
return &m_pageList[index];
}
return 0;
}
const QRollupCtrl::Page* QRollupCtrl::page(int index) const
{
if (index >= 0 && index < m_pageList.size())
{
return &m_pageList.at(index);
}
return 0;
}
inline void QRollupCtrl::Page::setText(const QString& text) { button->setText(text); }
inline void QRollupCtrl::Page::setIcon(const QIcon& is) { button->setIcon(is); }
inline void QRollupCtrl::Page::setToolTip(const QString& tip) { button->setToolTip(tip); }
inline QString QRollupCtrl::Page::text() const { return button->text(); }
inline QIcon QRollupCtrl::Page::icon() const { return button->icon(); }
inline QString QRollupCtrl::Page::toolTip() const { return button->toolTip(); }
//////////////////////////////////////////////////////////////////////////
QRollupCtrl::QRollupCtrl(QWidget* parent)
: QScrollArea(parent)
, m_layout(0)
{
m_body = new QWidget(this);
m_body->setBackgroundRole(QPalette::Button);
setWidgetResizable(true);
setAlignment(Qt::AlignLeft | Qt::AlignTop);
setWidget(m_body);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
relayout();
}
QRollupCtrl::~QRollupCtrl()
{
foreach(const QRollupCtrl::Page & c, m_pageList)
disconnect(c.widget, &QObject::destroyed, this, &QRollupCtrl::_q_widgetDestroyed);
}
void QRollupCtrl::readSettings(const QString& qSettingsGroup)
{
QSettings settings;
settings.beginGroup(qSettingsGroup);
int i = 0;
foreach(const QRollupCtrl::Page & c, m_pageList) {
QString qObjectName = c.widget->objectName();
bool bHidden = settings.value(qObjectName, true).toBool();
setIndexVisible(i++, !bHidden);
}
settings.endGroup();
}
void QRollupCtrl::writeSettings(const QString& qSettingsGroup)
{
QSettings settings;
settings.beginGroup(qSettingsGroup);
for (int i = 0; i < count(); i++)
{
QString qObjectName;
bool bHidden = isPageHidden(i, qObjectName);
settings.setValue(qObjectName, bHidden);
}
}
void QRollupCtrl::updateTabs()
{
QRollupCtrlButton* lastButton = 0;
for (auto i = m_pageList.constBegin(); i != m_pageList.constEnd(); ++i)
{
QRollupCtrlButton* tB = (*i).button;
QWidget* tW = (*i).sv;
tB->setSelected(tW->isVisible());
tB->update();
}
}
int QRollupCtrl::insertItem(int index, QWidget* widget, const QIcon& icon, const QString& text)
{
if (!widget)
{
return -1;
}
auto it = std::find_if(m_pageList.cbegin(), m_pageList.cend(), [widget](const Page& page) { return page.widget == widget; });
if (it != m_pageList.cend())
{
return -1;
}
connect(widget, &QObject::destroyed, this, &QRollupCtrl::_q_widgetDestroyed);
QRollupCtrl::Page c;
c.widget = widget;
c.button = new QRollupCtrlButton(m_body);
c.button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(c.button, &QRollupCtrlButton::clicked, this, &QRollupCtrl::_q_buttonClicked);
connect(c.button, &QRollupCtrlButton::customContextMenuRequested, this, &QRollupCtrl::_q_custumButtonMenu);
c.sv = new QFrame(m_body);
c.sv->setObjectName("rollupPaneFrame");
// c.sv->setFixedHeight(qMax(widget->sizeHint().height(), widget->size().height()));
QVBoxLayout* layout = new QVBoxLayout;
layout->setMargin(3);
layout->addWidget(widget);
c.sv->setLayout(layout);
c.sv->setStyleSheet("QFrame#rollupPaneFrame {margin: 0px 2px 2px 2px; border: 1px solid #84807D; border-top:0px;}");
c.sv->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
c.sv->show();
c.setText(text);
c.setIcon(icon);
const int numPages = m_pageList.count();
if (index < 0 || index >= numPages)
{
m_pageList.append(c);
index = numPages - 1;
m_layout->insertWidget(m_layout->count() - 1, c.button);
m_layout->insertWidget(m_layout->count() - 1, c.sv);
}
else
{
m_pageList.insert(index, c);
relayout();
}
c.button->show();
updateTabs();
itemInserted(index);
return index;
}
void QRollupCtrl::_q_buttonClicked()
{
QObject* tb = sender();
QWidget* item = 0;
for (auto i = m_pageList.constBegin(); i != m_pageList.constEnd(); ++i)
{
if ((*i).button == tb)
{
item = (*i).widget;
break;
}
}
if (item)
{
setIndexVisible(indexOf(item), !item->isVisible());
}
}
int QRollupCtrl::count() const
{
return m_pageList.count();
}
bool QRollupCtrl::isPageHidden(int index, QString& qObjectName) const
{
if (index < 0 || index >= m_pageList.size())
{
return true;
}
const QRollupCtrl::Page& c = m_pageList.at(index);
qObjectName = c.widget->objectName();
return c.sv->isHidden();
}
void QRollupCtrl::setIndexVisible(int index, bool visible)
{
QRollupCtrl::Page* c = page(index);
if (!c)
{
return;
}
if (c->sv->isHidden() && visible)
{
c->sv->show();
}
else if (c->sv->isVisible() && !visible)
{
c->sv->hide();
}
updateTabs();
}
void QRollupCtrl::setWidgetVisible(QWidget* widget, bool visible)
{
setIndexVisible(indexOf(widget), visible);
}
void QRollupCtrl::relayout()
{
delete m_layout;
m_layout = new QVBoxLayout(m_body);
m_layout->setMargin(3);
m_layout->setSpacing(0);
for (QRollupCtrl::PageList::ConstIterator i = m_pageList.constBegin(); i != m_pageList.constEnd(); ++i)
{
m_layout->addWidget((*i).button);
m_layout->addWidget((*i).sv);
}
m_layout->addStretch();
updateTabs();
}
void QRollupCtrl::_q_widgetDestroyed(QObject* object)
{
// no verification - vtbl corrupted already
QWidget* p = (QWidget*)object;
QRollupCtrl::Page* c = page(p);
if (!p || !c)
{
return;
}
m_layout->removeWidget(c->sv);
m_layout->removeWidget(c->button);
c->sv->deleteLater(); // page might still be a child of sv
delete c->button;
m_pageList.removeOne(*c);
}
void QRollupCtrl::_q_custumButtonMenu([[maybe_unused]] const QPoint& pos)
{
QMenu menu;
menu.addAction("Expand All")->setData(-1);
menu.addAction("Collapse All")->setData(-2);
menu.addSeparator();
for (int i = 0; i < m_pageList.size(); ++i)
{
QRollupCtrl::Page* c = page(i);
QAction* action = menu.addAction(c->button->text());
action->setCheckable(true);
action->setChecked(c->sv->isVisible());
action->setData(i);
}
QAction* action = menu.exec(QCursor::pos());
if (!action)
{
return;
}
int res = action->data().toInt();
switch (res)
{
case -1: // fall through
case -2:
expandAllPages(res == -1);
break;
default:
{
QRollupCtrl::Page* c = page(res);
if (c)
{
setIndexVisible(res, !c->sv->isVisible());
}
}
break;
}
}
void QRollupCtrl::expandAllPages(bool v)
{
for (int i = 0; i < m_pageList.size(); i++)
{
setIndexVisible(i, v);
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void QRollupCtrl::clear()
{
while (!m_pageList.isEmpty())
{
removeItem(0);
}
}
void QRollupCtrl::removeItem(QWidget* widget)
{
auto it = std::find_if(m_pageList.cbegin(), m_pageList.cend(), [widget](const Page& page) { return page.widget == widget; });
if (it != m_pageList.cend())
{
removeItem(it - m_pageList.cbegin());
}
}
void QRollupCtrl::removeItem(int index)
{
if (QWidget* w = widget(index))
{
disconnect(w, &QObject::destroyed, this, &QRollupCtrl::_q_widgetDestroyed);
w->setParent(this);
// destroy internal data
_q_widgetDestroyed(w);
itemRemoved(index);
}
}
QWidget* QRollupCtrl::widget(int index) const
{
if (index < 0 || index >= (int) m_pageList.size())
{
return 0;
}
return m_pageList.at(index).widget;
}
int QRollupCtrl::indexOf(QWidget* widget) const
{
QRollupCtrl::Page* c = page(widget);
return c ? m_pageList.indexOf(*c) : -1;
}
void QRollupCtrl::setItemEnabled(int index, bool enabled)
{
QRollupCtrl::Page* c = page(index);
if (!c)
{
return;
}
c->button->setEnabled(enabled);
if (!enabled)
{
int curIndexUp = index;
int curIndexDown = curIndexUp;
const int count = m_pageList.count();
while (curIndexUp > 0 || curIndexDown < count - 1)
{
if (curIndexDown < count - 1)
{
if (page(++curIndexDown)->button->isEnabled())
{
index = curIndexDown;
break;
}
}
if (curIndexUp > 0)
{
if (page(--curIndexUp)->button->isEnabled())
{
index = curIndexUp;
break;
}
}
}
}
}
void QRollupCtrl::setItemText(int index, const QString& text)
{
QRollupCtrl::Page* c = page(index);
if (c)
{
c->setText(text);
}
}
void QRollupCtrl::setItemIcon(int index, const QIcon& icon)
{
QRollupCtrl::Page* c = page(index);
if (c)
{
c->setIcon(icon);
}
}
void QRollupCtrl::setItemToolTip(int index, const QString& toolTip)
{
QRollupCtrl::Page* c = page(index);
if (c)
{
c->setToolTip(toolTip);
}
}
bool QRollupCtrl::isItemEnabled(int index) const
{
const QRollupCtrl::Page* c = page(index);
return c && c->button->isEnabled();
}
QString QRollupCtrl::itemText(int index) const
{
const QRollupCtrl::Page* c = page(index);
return (c ? c->text() : QString());
}
QIcon QRollupCtrl::itemIcon(int index) const
{
const QRollupCtrl::Page* c = page(index);
return (c ? c->icon() : QIcon());
}
QString QRollupCtrl::itemToolTip(int index) const
{
const QRollupCtrl::Page* c = page(index);
return (c ? c->toolTip() : QString());
}
void QRollupCtrl::changeEvent(QEvent* ev)
{
if (ev->type() == QEvent::StyleChange)
{
updateTabs();
}
QFrame::changeEvent(ev);
}
void QRollupCtrl::showEvent(QShowEvent* ev)
{
if (isVisible())
{
updateTabs();
}
IEditor* pEditor = GetIEditor();
pEditor->SetEditMode(EEditMode::eEditModeSelect);
QFrame::showEvent(ev);
}
void QRollupCtrl::itemInserted(int index)
{
Q_UNUSED(index)
}
void QRollupCtrl::itemRemoved(int index)
{
Q_UNUSED(index)
}
#include <Controls/moc_QRollupCtrl.cpp>
+126
View File
@@ -0,0 +1,126 @@
#ifndef CRYINCLUDE_EDITOR_CONTROLS_QROLLUPCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_QROLLUPCTRL_H
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QFrame>
#include <QScrollArea>
#include <QIcon>
#endif
class QVBoxLayout;
class QRollupCtrlButton;
class QRollupCtrl
: public QScrollArea
{
Q_OBJECT
Q_PROPERTY(int count READ count)
public:
explicit QRollupCtrl(QWidget* parent = 0);
~QRollupCtrl();
int addItem(QWidget* widget, const QString& text);
int addItem(QWidget* widget, const QIcon& icon, const QString& text);
int insertItem(int index, QWidget* widget, const QString& text);
int insertItem(int index, QWidget* widget, const QIcon& icon, const QString& text);
void clear();
void removeItem(QWidget* widget);
void removeItem(int index);
void setItemEnabled(int index, bool enabled);
bool isItemEnabled(int index) const;
void setItemText(int index, const QString& text);
QString itemText(int index) const;
void setItemIcon(int index, const QIcon& icon);
QIcon itemIcon(int index) const;
void setItemToolTip(int index, const QString& toolTip);
QString itemToolTip(int index) const;
QWidget* widget(int index) const;
int indexOf(QWidget* widget) const;
int count() const;
void readSettings (const QString& qSettingsGroup);
void writeSettings(const QString& qSettingsGroup);
public slots:
void setIndexVisible(int index, bool visible);
void setWidgetVisible(QWidget* widget, bool visible);
void expandAllPages(bool v);
protected:
virtual void itemInserted(int index);
virtual void itemRemoved(int index);
void changeEvent(QEvent*) override;
void showEvent(QShowEvent*) override;
private:
Q_DISABLE_COPY(QRollupCtrl)
struct Page
{
QRollupCtrlButton* button;
QFrame* sv;
QWidget* widget;
void setText(const QString& text);
void setIcon(const QIcon& is);
void setToolTip(const QString& tip);
QString text() const;
QIcon icon() const;
QString toolTip() const;
inline bool operator==(const Page& other) const
{
return widget == other.widget;
}
};
typedef QList<Page> PageList;
Page* page(QWidget* widget) const;
const Page* page(int index) const;
Page* page(int index);
void updateTabs();
void relayout();
bool isPageHidden(int index, QString& qObjectName) const;
QWidget* m_body;
PageList m_pageList;
QVBoxLayout* m_layout;
private slots:
void _q_buttonClicked();
void _q_widgetDestroyed(QObject*);
void _q_custumButtonMenu(const QPoint&);
};
//////////////////////////////////////////////////////////////////////////
inline int QRollupCtrl::addItem(QWidget* item, const QString& text)
{ return insertItem(-1, item, QIcon(), text); }
inline int QRollupCtrl::addItem(QWidget* item, const QIcon& iconSet, const QString& text)
{ return insertItem(-1, item, iconSet, text); }
inline int QRollupCtrl::insertItem(int index, QWidget* item, const QString& text)
{ return insertItem(index, item, QIcon(), text); }
#endif
@@ -0,0 +1,648 @@
/*
* 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 "EditorDefs.h"
#include <Controls/QToolTipWidget.h>
#include "QBitmapPreviewDialogImp.h"
#include "qcoreapplication.h"
#include "qguiapplication.h"
#include "qapplication.h"
#include <QDesktopWidget>
#include <QPainter>
#include <QtGlobal>
#include <qgraphicseffect.h>
void QToolTipWidget::RebuildLayout()
{
if (m_title != nullptr)
{
m_title->hide();
}
if (m_content != nullptr)
{
m_content->hide();
}
if (m_specialContent != nullptr)
{
m_specialContent->hide();
}
//empty layout
while (m_layout->count() > 0)
{
m_layout->takeAt(0);
}
qDeleteAll(m_currentShortcuts);
m_currentShortcuts.clear();
if (m_includeTextureShortcuts)
{
m_currentShortcuts.append(new QLabel(tr("Alt - Alpha"), this));
m_currentShortcuts.back()->setProperty("tooltipLabel", "Shortcut");
m_currentShortcuts.append(new QLabel(tr("Shift - RGBA"), this));
m_currentShortcuts.back()->setProperty("tooltipLabel", "Shortcut");
}
if (m_title != nullptr && !m_title->text().isEmpty())
{
m_layout->addWidget(m_title);
m_title->show();
}
for (QLabel* var : m_currentShortcuts)
{
if (var != nullptr)
{
m_layout->addWidget(var);
var->show();
}
}
if (m_specialContent != nullptr)
{
m_layout->addWidget(m_specialContent);
m_specialContent->show();
}
if (m_content != nullptr && !m_content->text().isEmpty())
{
m_layout->addWidget(m_content);
m_content->show();
}
m_background->adjustSize();
adjustSize();
}
void QToolTipWidget::Hide()
{
m_currentShortcuts.clear();
hide();
}
void QToolTipWidget::Show(QPoint pos, ArrowDirection dir)
{
if (!IsValid())
{
return;
}
m_arrow->m_direction = dir;
pos = AdjustTipPosByArrowSize(pos, dir);
m_normalPos = pos;
move(pos);
RebuildLayout();
show();
m_arrow->show();
}
void QToolTipWidget::Display(QRect targetRect, ArrowDirection preferredArrowDir)
{
if (!IsValid())
{
return;
}
KeepTipOnScreen(targetRect, preferredArrowDir);
RebuildLayout();
show();
m_arrow->show();
}
void QToolTipWidget::TryDisplay(QPoint mousePos, const QRect& rect, [[maybe_unused]] ArrowDirection preferredArrowDir)
{
if (rect.contains(mousePos))
{
Display(rect, QToolTipWidget::ArrowDirection::ARROW_RIGHT);
}
else
{
hide();
}
}
void QToolTipWidget::TryDisplay(QPoint mousePos, const QWidget* widget, ArrowDirection preferredArrowDir)
{
const QRect rect(widget->mapToGlobal(QPoint(0,0)), widget->size());
TryDisplay(mousePos, rect, preferredArrowDir);
}
void QToolTipWidget::SetTitle(QString title)
{
if (!title.isEmpty())
{
m_title->setText(title);
}
m_title->setProperty("tooltipLabel", "Title");
setWindowTitle("ToolTip - " + title);
}
void QToolTipWidget::SetContent(QString content)
{
m_content->setWordWrap(true);
m_content->setProperty("tooltipLabel", "Content");
//line-height is not supported via stylesheet so we use the html rich-text subset in QT for it.
m_content->setText(QString("<span style=\"line-height: 14px;\">%1</span>").arg(content));
}
void QToolTipWidget::AppendContent(QString content)
{
m_content->setText(m_content->text() + "\n\n" + content);
update();
RebuildLayout();
m_content->update();
m_content->repaint();
}
QToolTipWidget::QToolTipWidget(QWidget* parent)
: QWidget(parent)
{
m_background = new QWidget(this);
m_background->setProperty("tooltip", "Background");
m_background->stackUnder(this);
m_title = new QLabel(this);
m_currentShortcuts = QVector<QLabel*>();
m_content = new QLabel(this);
m_specialContent = nullptr;
setWindowTitle("ToolTip");
setObjectName("ToolTip");
m_layout = new QVBoxLayout(this);
m_normalPos = QPoint(0, 0);
m_arrow = new QArrow(m_background);
setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint);
m_arrow->setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint);
m_arrow->setAttribute(Qt::WA_TranslucentBackground, true);
m_background->setLayout(m_layout);
m_arrow->setObjectName("ToolTipArrow");
m_background->setObjectName("ToolTipBackground");
//we need a drop shadow for the background
QGraphicsDropShadowEffect* dropShadow = new QGraphicsDropShadowEffect(this);
dropShadow->setBlurRadius(m_shadowRadius);
dropShadow->setColor(Qt::black);
dropShadow->setOffset(0);
dropShadow->setEnabled(true);
m_background->setGraphicsEffect(dropShadow);
//we need a second drop shadow effect for the arrow
dropShadow = new QGraphicsDropShadowEffect(m_arrow);
dropShadow->setBlurRadius(m_shadowRadius);
dropShadow->setColor(Qt::black);
dropShadow->setOffset(0);
dropShadow->setEnabled(true);
m_arrow->setGraphicsEffect(dropShadow);
}
QToolTipWidget::~QToolTipWidget()
{
}
void QToolTipWidget::AddSpecialContent(QString type, QString dataStream)
{
if (type.isEmpty())
{
m_includeTextureShortcuts = false;
if (m_specialContent != nullptr)
{
delete m_specialContent;
m_specialContent = nullptr;
}
return;
}
if (type == "TEXTURE")
{
if (m_specialContent == nullptr)
{
QCoreApplication::instance()->installEventFilter(this); //grab the event filter while displaying the advanced texture tooltip
m_specialContent = new QBitmapPreviewDialogImp(this);
}
QString path(dataStream);
qobject_cast<QBitmapPreviewDialogImp*>(m_specialContent)->setImage(path);
// set default showmode to RGB
qobject_cast<QBitmapPreviewDialogImp*>(m_specialContent)->setShowMode(QBitmapPreviewDialogImp::EShowMode::ESHOW_RGB);
QString dir = (path.split("/").count() > path.split("\\").count()) ? path.split("/").back() : path.split("\\").back();
SetTitle(dir);
//always use default size but not image size
qobject_cast<QBitmapPreviewDialogImp*>(m_specialContent)->setOriginalSize(false);
m_includeTextureShortcuts = true;
}
else if (type == "ADD TO CONTENT")
{
AppendContent(dataStream);
m_includeTextureShortcuts = false;
if (m_specialContent != nullptr)
{
delete m_specialContent;
m_specialContent = nullptr;
}
}
else if (type == "REPLACE TITLE")
{
SetTitle(dataStream);
m_includeTextureShortcuts = false;
if (m_specialContent != nullptr)
{
delete m_specialContent;
m_specialContent = nullptr;
}
}
else if (type == "REPLACE CONTENT")
{
SetContent(dataStream);
m_includeTextureShortcuts = false;
if (m_specialContent != nullptr)
{
delete m_specialContent;
m_specialContent = nullptr;
}
}
else
{
m_includeTextureShortcuts = false;
if (m_specialContent != nullptr)
{
delete m_specialContent;
m_specialContent = nullptr;
}
return;
}
m_special = type;
}
bool QToolTipWidget::eventFilter(QObject* obj, QEvent* event)
{
if (event->type() == QEvent::KeyPress)
{
if (m_special == "TEXTURE" && m_specialContent != nullptr)
{
const QKeyEvent* ke = static_cast<QKeyEvent*>(event);
Qt::KeyboardModifiers mods = ke->modifiers();
if (mods & Qt::KeyboardModifier::AltModifier)
{
((QBitmapPreviewDialogImp*)m_specialContent)->setShowMode(QBitmapPreviewDialogImp::ESHOW_ALPHA);
}
else if (mods & Qt::KeyboardModifier::ShiftModifier && !(mods & Qt::KeyboardModifier::ControlModifier))
{
((QBitmapPreviewDialogImp*)m_specialContent)->setShowMode(QBitmapPreviewDialogImp::ESHOW_RGBA);
}
}
}
if (event->type() == QEvent::KeyRelease)
{
if (m_special == "TEXTURE" && m_specialContent != nullptr)
{
const QKeyEvent* ke = static_cast<QKeyEvent*>(event);
Qt::KeyboardModifiers mods = ke->modifiers();
if (!(mods& Qt::KeyboardModifier::AltModifier) && !(mods & Qt::KeyboardModifier::ShiftModifier))
{
((QBitmapPreviewDialogImp*)m_specialContent)->setShowMode(QBitmapPreviewDialogImp::ESHOW_RGB);
}
}
}
return QWidget::eventFilter(obj, event);
}
void QToolTipWidget::hideEvent(QHideEvent* event)
{
QWidget::hideEvent(event);
m_arrow->hide();
}
void QToolTipWidget::UpdateOptionalData(QString optionalData)
{
AddSpecialContent(m_special, optionalData);
}
QPoint QToolTipWidget::AdjustTipPosByArrowSize(QPoint pos, ArrowDirection dir)
{
switch (dir)
{
case QToolTipWidget::ArrowDirection::ARROW_UP:
{
m_arrow->move(pos);
pos.setY(pos.y() + 10);
m_arrow->setFixedSize(20, 10);
pos -= QPoint(m_shadowRadius, m_shadowRadius);
break;
}
case QToolTipWidget::ArrowDirection::ARROW_LEFT:
{
m_arrow->move(pos);
pos.setX(pos.x() + 10);
m_arrow->setFixedSize(10, 20);
pos -= QPoint(m_shadowRadius, m_shadowRadius);
break;
}
case QToolTipWidget::ArrowDirection::ARROW_RIGHT:
{
pos.setX(pos.x() - 10);
m_arrow->move(QPoint(pos.x() + width(), pos.y()));
m_arrow->setFixedSize(10, 20);
pos -= QPoint(-m_shadowRadius, m_shadowRadius);
break;
}
case QToolTipWidget::ArrowDirection::ARROW_DOWN:
{
pos.setY(pos.y() - 10);
m_arrow->move(QPoint(pos.x(), pos.y() + height()));
m_arrow->setFixedSize(20, 10);
pos -= QPoint(m_shadowRadius, -m_shadowRadius);
break;
}
default:
m_arrow->move(-10, -10);
break;
}
return pos;
}
bool QToolTipWidget::IsValid()
{
if (m_title->text().isEmpty() ||
(m_content->text().isEmpty() && m_specialContent == nullptr))
{
return false;
}
return true;
}
void QToolTipWidget::KeepTipOnScreen(QRect targetRect, ArrowDirection preferredArrowDir)
{
QRect desktop = QApplication::desktop()->availableGeometry(this);
if (this->isHidden())
{
setAttribute(Qt::WA_DontShowOnScreen, true);
Show(QPoint(0, 0), preferredArrowDir);
hide();
setAttribute(Qt::WA_DontShowOnScreen, false);
}
//else assume the size is right
//calculate initial rect
QRect tipRect = QRect(0, 0, 0, 0);
switch (preferredArrowDir)
{
case QToolTipWidget::ArrowDirection::ARROW_UP:
{
//tip is below the widget with a left alignment
tipRect.setTopLeft(AdjustTipPosByArrowSize(targetRect.bottomLeft(), preferredArrowDir));
break;
}
case QToolTipWidget::ArrowDirection::ARROW_LEFT:
{
//tip is on the right with the top being even
tipRect.setTopLeft(AdjustTipPosByArrowSize(targetRect.topRight(), preferredArrowDir));
break;
}
case QToolTipWidget::ArrowDirection::ARROW_RIGHT:
{
//tip is on the left with the top being even
tipRect.setY(targetRect.top());
tipRect.setX(targetRect.left() - width());
tipRect.setTopLeft(AdjustTipPosByArrowSize(tipRect.topLeft(), preferredArrowDir));
break;
}
case QToolTipWidget::ArrowDirection::ARROW_DOWN:
{
//tip is above the widget with a left alignment
tipRect.setX(targetRect.left());
tipRect.setY(targetRect.top() - height());
tipRect.setTopLeft(AdjustTipPosByArrowSize(tipRect.topLeft(), preferredArrowDir));
break;
}
default:
{
//tip is on the right with the top being even
preferredArrowDir = QToolTipWidget::ArrowDirection::ARROW_LEFT;
tipRect.setTopLeft(AdjustTipPosByArrowSize(targetRect.topRight(), QToolTipWidget::ArrowDirection::ARROW_LEFT));
break;
}
}
tipRect.setSize(size());
//FixPositioning
if (preferredArrowDir == ArrowDirection::ARROW_LEFT || preferredArrowDir == ArrowDirection::ARROW_RIGHT)
{
if (tipRect.left() <= desktop.left())
{
m_arrow->m_direction = ArrowDirection::ARROW_LEFT;
tipRect.setTopLeft(AdjustTipPosByArrowSize(targetRect.topRight(), m_arrow->m_direction));
}
else if (tipRect.right() >= desktop.right())
{
m_arrow->m_direction = ArrowDirection::ARROW_RIGHT;
tipRect.setLeft(targetRect.left() - width());
tipRect.setTopLeft(AdjustTipPosByArrowSize(tipRect.topLeft(), m_arrow->m_direction));
}
}
else if (preferredArrowDir == ArrowDirection::ARROW_UP || preferredArrowDir == ArrowDirection::ARROW_DOWN)
{
if (tipRect.top() <= desktop.top())
{
m_arrow->m_direction = ArrowDirection::ARROW_UP;
tipRect.setTopLeft(AdjustTipPosByArrowSize(targetRect.bottomLeft(), m_arrow->m_direction));
}
else if (tipRect.bottom() >= desktop.bottom())
{
m_arrow->m_direction = ArrowDirection::ARROW_DOWN;
tipRect.setY(targetRect.top() - height());
tipRect.setTopLeft(AdjustTipPosByArrowSize(tipRect.topLeft(), m_arrow->m_direction));
}
}
//Nudge tip without arrow
if (preferredArrowDir == ArrowDirection::ARROW_UP || preferredArrowDir == ArrowDirection::ARROW_DOWN)
{
if (tipRect.left() <= desktop.left())
{
tipRect.setLeft(desktop.left());
}
else if (tipRect.right() >= desktop.right())
{
tipRect.setLeft(desktop.right() - width());
}
}
else if (preferredArrowDir == ArrowDirection::ARROW_RIGHT || preferredArrowDir == ArrowDirection::ARROW_LEFT)
{
if (tipRect.top() <= desktop.top())
{
tipRect.setTop(desktop.top());
}
else if (tipRect.bottom() >= desktop.bottom())
{
tipRect.setTop(desktop.bottom() - height());
}
}
m_normalPos = tipRect.topLeft();
move(m_normalPos);
}
QPolygonF QToolTipWidget::QArrow::CreateArrow()
{
int height = 10;
QVector<QPointF> vertex;
//3 points in triangle
vertex.reserve(3);
//all magic number below are given in order to draw smooth transitions between tooltip and arrow
if (m_direction == ArrowDirection::ARROW_UP)
{
vertex.push_back(QPointF(10, 1));
vertex.push_back(QPointF(19, 10));
vertex.push_back(QPointF(0, 10));
}
else if (m_direction == ArrowDirection::ARROW_RIGHT)
{
vertex.push_back(QPointF(9, 10));
vertex.push_back(QPointF(0, 19));
vertex.push_back(QPointF(0, 1));
}
else if (m_direction == ArrowDirection::ARROW_LEFT)
{
vertex.push_back(QPointF(1, 10));
vertex.push_back(QPointF(10, 19));
vertex.push_back(QPointF(10, 0));
}
else //ArrowDirection::ARROW_DOWN
{
vertex.push_back(QPointF(10, 10));
vertex.push_back(QPointF(19, 0));
vertex.push_back(QPointF(0, 0));
}
return QPolygonF(vertex);
}
void QToolTipWidget::QArrow::paintEvent([[maybe_unused]] QPaintEvent* event)
{
QColor color(255, 255, 255, 255);
QPainter painter(this);
painter.fillRect(rect(), Qt::transparent); //force transparency
painter.setRenderHint(QPainter::Antialiasing, false);
painter.setBrush(color);
painter.setPen(Qt::NoPen);
painter.drawPolygon(CreateArrow());
//painter.setRenderHint(QPainter::Antialiasing, false);
}
QToolTipWrapper::QToolTipWrapper(QWidget* parent)
: QObject(parent)
{
}
void QToolTipWrapper::SetTitle(QString title)
{
m_title = title;
}
void QToolTipWrapper::SetContent(QString content)
{
AddSpecialContent("REPLACE CONTENT", content);
}
void QToolTipWrapper::AppendContent(QString content)
{
AddSpecialContent("ADD TO CONTENT", content);
}
void QToolTipWrapper::AddSpecialContent(QString type, QString dataStream)
{
if (type == "REPLACE CONTENT")
{
m_contentOperations.clear();
}
m_contentOperations.push_back({type, dataStream});
}
void QToolTipWrapper::UpdateOptionalData(QString optionalData)
{
m_contentOperations.push_back({"UPDATE OPTIONAL", optionalData});
}
void QToolTipWrapper::Display(QRect targetRect, QToolTipWidget::ArrowDirection preferredArrowDir)
{
GetOrCreateToolTip()->Display(targetRect, preferredArrowDir);
}
void QToolTipWrapper::TryDisplay(QPoint mousePos, const QWidget * widget, QToolTipWidget::ArrowDirection preferredArrowDir)
{
GetOrCreateToolTip()->TryDisplay(mousePos, widget, preferredArrowDir);
}
void QToolTipWrapper::TryDisplay(QPoint mousePos, const QRect & widget, QToolTipWidget::ArrowDirection preferredArrowDir)
{
GetOrCreateToolTip()->TryDisplay(mousePos, widget, preferredArrowDir);
}
void QToolTipWrapper::hide()
{
DestroyToolTip();
}
void QToolTipWrapper::show()
{
GetOrCreateToolTip()->show();
}
bool QToolTipWrapper::isVisible() const
{
return m_actualTooltip && m_actualTooltip->isVisible();
}
void QToolTipWrapper::update()
{
if (m_actualTooltip)
{
m_actualTooltip->update();
}
}
void QToolTipWrapper::ReplayContentOperations(QToolTipWidget* tooltipWidget)
{
tooltipWidget->SetTitle(m_title);
for (const auto& operation : m_contentOperations)
{
if (operation.first == "UPDATE OPTIONAL")
{
tooltipWidget->UpdateOptionalData(operation.second);
}
else
{
tooltipWidget->AddSpecialContent(operation.first, operation.second);
}
}
}
QToolTipWidget * QToolTipWrapper::GetOrCreateToolTip()
{
if (!m_actualTooltip)
{
QToolTipWidget* tooltipWidget = new QToolTipWidget(static_cast<QWidget*>(parent()));
tooltipWidget->setAttribute(Qt::WA_DeleteOnClose);
ReplayContentOperations(tooltipWidget);
m_actualTooltip = tooltipWidget;
}
return m_actualTooltip.data();
}
void QToolTipWrapper::DestroyToolTip()
{
if (m_actualTooltip)
{
m_actualTooltip->deleteLater();
}
}
@@ -0,0 +1,152 @@
/*
* 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.
*
*/
#ifndef QToolTipWidget_h__
#define QToolTipWidget_h__
#include "EditorCoreAPI.h"
#include <QPointer>
#include <QWidget>
#include <QLabel>
#include <QString>
#include <QMap>
#include <QMapIterator>
#include <QVector>
#include <QVBoxLayout>
#include <memory>
class IQToolTip
{
public:
virtual void SetTitle(QString title) = 0;
virtual void SetContent(QString content) = 0;
virtual void AppendContent(QString content) = 0;
virtual void AddSpecialContent(QString type, QString dataStream) = 0;
virtual void UpdateOptionalData(QString optionalData) = 0;
};
class EDITOR_CORE_API QToolTipWidget
: public QWidget
, public IQToolTip
{
public:
enum class ArrowDirection
{
ARROW_UP,
ARROW_LEFT,
ARROW_RIGHT,
ARROW_DOWN
};
class QArrow
: public QWidget
{
public:
ArrowDirection m_direction;
QPoint m_pos;
QArrow(QWidget* parent)
: QWidget(parent){ setWindowFlags(Qt::ToolTip); }
virtual ~QArrow(){}
QPolygonF CreateArrow();
virtual void paintEvent(QPaintEvent*) override;
};
QToolTipWidget(QWidget* parent);
~QToolTipWidget();
void SetTitle(QString title) override;
void SetContent(QString content) override;
void AppendContent(QString content) override;
void AddSpecialContent(QString type, QString dataStream) override;
void UpdateOptionalData(QString optionalData) override;
void Display(QRect targetRect, ArrowDirection preferredArrowDir);
//! Displays the tooltip on the given widget, only if the mouse is over it.
void TryDisplay(QPoint mousePos, const QWidget* widget, ArrowDirection preferredArrowDir);
//! Displays the tooltip on the given rect, only if the mouse is over it.
void TryDisplay(QPoint mousePos, const QRect& widget, ArrowDirection preferredArrowDir);
void Hide();
protected:
void Show(QPoint pos, ArrowDirection dir);
bool IsValid();
void KeepTipOnScreen(QRect targetRect, ArrowDirection preferredArrowDir);
QPoint AdjustTipPosByArrowSize(QPoint pos, ArrowDirection dir);
virtual bool eventFilter(QObject* obj, QEvent* event) override;
void RebuildLayout();
virtual void hideEvent(QHideEvent*) override;
QLabel* m_title;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QVector<QLabel*> m_currentShortcuts;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
//can be anything from QLabel to QBitMapPreviewDialog
//must allow movement, and show/hide calls
QLabel* m_content;
QWidget* m_specialContent;
QWidget* m_background;
QVBoxLayout* m_layout;
QString m_special;
QPoint m_normalPos;
QArrow* m_arrow;
const int m_shadowRadius = 5;
bool m_includeTextureShortcuts; //added since Qt does not support modifier only shortcuts
};
// HACK: The EditorUI_QT classes all were keeping persistent references to QToolTipWidgets around
// This led to many, many top-level widget creations, which led to many platform-side window allocations
// which led to crashes in Qt5.15. As this is legacy code, this is a drop-in replacement that only
// allocates the actual QToolTipWidget (and thus platform window) while the tooltip is visible
class EDITOR_CORE_API QToolTipWrapper
: public QObject
, public IQToolTip
{
public:
QToolTipWrapper(QWidget* parent);
void SetTitle(QString title) override;
void SetContent(QString content) override;
void AppendContent(QString content) override;
void AddSpecialContent(QString type, QString dataStream) override;
void UpdateOptionalData(QString optionalData) override;
void Display(QRect targetRect, QToolTipWidget::ArrowDirection preferredArrowDir);
void TryDisplay(QPoint mousePos, const QWidget* widget, QToolTipWidget::ArrowDirection preferredArrowDir);
void TryDisplay(QPoint mousePos, const QRect& widget, QToolTipWidget::ArrowDirection preferredArrowDir);
void hide();
void show();
bool isVisible() const;
void update();
void repaint(){update();} //Things really shouldn't be calling repaint on these...
void Hide(){hide();}
void close(){hide();}
private:
void ReplayContentOperations(QToolTipWidget* tooltipWidget);
QToolTipWidget* GetOrCreateToolTip();
void DestroyToolTip();
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // conditional expression is constant, needs to have dll-interface to be used by clients of class 'AzQtComponents::FilteredSearchWidget'
QPointer<QToolTipWidget> m_actualTooltip;
AZ_POP_DISABLE_WARNING
QString m_title;
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // conditional expression is constant, needs to have dll-interface to be used by clients of class 'AzQtComponents::FilteredSearchWidget'
QVector<QPair<QString, QString>> m_contentOperations;
AZ_POP_DISABLE_WARNING
};
#endif // QToolTipWidget_h__
@@ -0,0 +1,136 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 "EditorDefs.h"
#include "PropertyAnimationCtrl.h"
// Qt
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
// Editor
#include "Util/UIEnumerations.h"
#include "IResourceSelectorHost.h"
AnimationPropertyCtrl::AnimationPropertyCtrl(QWidget *pParent)
: QWidget(pParent)
{
m_animationLabel = new QLabel;
m_pApplyButton = new QToolButton;
m_pApplyButton->setIcon(QIcon(":/reflectedPropertyCtrl/img/apply.png"));
m_pApplyButton->setFocusPolicy(Qt::StrongFocus);
QHBoxLayout *pLayout = new QHBoxLayout(this);
pLayout->setContentsMargins(0, 0, 0, 0);
pLayout->addWidget(m_animationLabel, 1);
pLayout->addWidget(m_pApplyButton);
connect(m_pApplyButton, &QAbstractButton::clicked, this, &AnimationPropertyCtrl::OnApplyClicked);
};
AnimationPropertyCtrl::~AnimationPropertyCtrl()
{
}
void AnimationPropertyCtrl::SetValue(const CReflectedVarAnimation &animation)
{
m_animation = animation;
m_animationLabel->setText(animation.m_animation.c_str());
}
CReflectedVarAnimation AnimationPropertyCtrl::value() const
{
return m_animation;
}
void AnimationPropertyCtrl::OnApplyClicked()
{
CUIEnumerations &roGeneralProxy = CUIEnumerations::GetUIEnumerationsInstance();
QStringList cSelectedAnimations;
size_t nTotalAnimations(0);
size_t nCurrentAnimation(0);
QString combinedString = GetIEditor()->GetResourceSelectorHost()->GetGlobalSelection("animation");
SplitString(combinedString, cSelectedAnimations, ',');
nTotalAnimations = cSelectedAnimations.size();
for (nCurrentAnimation = 0; nCurrentAnimation < nTotalAnimations; ++nCurrentAnimation)
{
QString& rstrCurrentAnimAction = cSelectedAnimations[nCurrentAnimation];
if (!rstrCurrentAnimAction.isEmpty())
{
m_animation.m_animation = rstrCurrentAnimAction.toUtf8().data();
m_animationLabel->setText(m_animation.m_animation.c_str());
emit ValueChanged(m_animation);
}
}
}
QWidget* AnimationPropertyCtrl::GetFirstInTabOrder()
{
return m_pApplyButton;
}
QWidget* AnimationPropertyCtrl::GetLastInTabOrder()
{
return m_pApplyButton;
}
void AnimationPropertyCtrl::UpdateTabOrder()
{
setTabOrder(m_pApplyButton, m_pApplyButton);
}
QWidget* AnimationPropertyWidgetHandler::CreateGUI(QWidget *pParent)
{
AnimationPropertyCtrl* newCtrl = aznew AnimationPropertyCtrl(pParent);
connect(newCtrl, &AnimationPropertyCtrl::ValueChanged, newCtrl, [newCtrl]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl);
});
return newCtrl;
}
void AnimationPropertyWidgetHandler::ConsumeAttribute(AnimationPropertyCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
Q_UNUSED(GUI);
Q_UNUSED(attrib);
Q_UNUSED(attrValue);
Q_UNUSED(debugName);
}
void AnimationPropertyWidgetHandler::WriteGUIValuesIntoProperty(size_t index, AnimationPropertyCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node)
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarAnimation val = GUI->value();
instance = static_cast<property_t>(val);
}
bool AnimationPropertyWidgetHandler::ReadValuesIntoGUI(size_t index, AnimationPropertyCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node)
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarAnimation val = instance;
GUI->SetValue(val);
return false;
}
#include <Controls/ReflectedPropertyControl/moc_PropertyAnimationCtrl.cpp>
@@ -0,0 +1,82 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_EDITOR_UTILS_PROPERTYANIMATIONCTRL_H
#define CRYINCLUDE_EDITOR_UTILS_PROPERTYANIMATIONCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include "ReflectedVar.h"
#include <QWidget>
#include <QPointer>
#endif
class QToolButton;
class QLabel;
class QHBoxLayout;
class AnimationPropertyCtrl
: public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(AnimationPropertyCtrl, AZ::SystemAllocator, 0);
AnimationPropertyCtrl(QWidget* pParent = nullptr);
virtual ~AnimationPropertyCtrl();
CReflectedVarAnimation value() const;
QWidget* GetFirstInTabOrder();
QWidget* GetLastInTabOrder();
void UpdateTabOrder();
signals:
void ValueChanged(CReflectedVarAnimation value);
public slots:
void SetValue(const CReflectedVarAnimation& animation);
protected slots:
void OnApplyClicked();
private:
QToolButton* m_pApplyButton;
QLabel* m_animationLabel;
CReflectedVarAnimation m_animation;
};
class AnimationPropertyWidgetHandler
: QObject
, public AzToolsFramework::PropertyHandler < CReflectedVarAnimation, AnimationPropertyCtrl >
{
public:
AZ_CLASS_ALLOCATOR(AnimationPropertyWidgetHandler, AZ::SystemAllocator, 0);
virtual AZ::u32 GetHandlerName(void) const override { return AZ_CRC("Animation", 0x8d5284dc); }
virtual bool IsDefaultHandler() const override { return true; }
virtual QWidget* GetFirstInTabOrder(AnimationPropertyCtrl* widget) override { return widget->GetFirstInTabOrder(); }
virtual QWidget* GetLastInTabOrder(AnimationPropertyCtrl* widget) override { return widget->GetLastInTabOrder(); }
virtual void UpdateWidgetInternalTabbing(AnimationPropertyCtrl* widget) override { widget->UpdateTabOrder(); }
virtual QWidget* CreateGUI(QWidget* pParent) override;
virtual void ConsumeAttribute(AnimationPropertyCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
virtual void WriteGUIValuesIntoProperty(size_t index, AnimationPropertyCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
virtual bool ReadValuesIntoGUI(size_t index, AnimationPropertyCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
};
#endif // CRYINCLUDE_EDITOR_UTILS_PROPERTYANIMATIONCTRL_H
@@ -0,0 +1,45 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 "EditorDefs.h"
// Editor
#include "PropertyCtrl.h"
#include "PropertyAnimationCtrl.h"
#include "PropertyResourceCtrl.h"
#include "PropertyGenericCtrl.h"
#include "PropertyMiscCtrl.h"
#include "PropertyMotionCtrl.h"
void RegisterReflectedVarHandlers()
{
static bool registered = false;
if (!registered)
{
registered = true;
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew AnimationPropertyWidgetHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew FileResourceSelectorWidgetHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew ShaderPropertyHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew MaterialPropertyHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew ReverbPresetPropertyHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew SequencePropertyHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew SequenceIdPropertyHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew LocalStringPropertyHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew LightAnimationPropertyHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew UserPopupWidgetHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew LensFlareHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew ColorCurveHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew FloatCurveHandler());
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew MotionPropertyWidgetHandler());
}
}
@@ -0,0 +1,22 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_EDITOR_UTILS_PROPERTYCTRL_H
#define CRYINCLUDE_EDITOR_UTILS_PROPERTYCTRL_H
#pragma once
void RegisterReflectedVarHandlers();
#endif // CRYINCLUDE_EDITOR_UTILS_PROPERTYCTRL_H
@@ -0,0 +1,296 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 "EditorDefs.h"
#include "PropertyGenericCtrl.h"
// Qt
#include <QMessageBox>
#include <QHBoxLayout>
#include <QtWidgets/QLabel>
#include <QLineEdit>
#include <QStringListModel>
#include <QToolButton>
// CryCommon
#include <CryCommon/ILocalizationManager.h>
// Editor
#include "ShadersDialog.h"
#include "Material/MaterialManager.h"
#include "SelectLightAnimationDialog.h"
#include "SelectSequenceDialog.h"
#include "SelectEAXPresetDlg.h"
#include "QtViewPaneManager.h"
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <QtWidgets/QListView>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
GenericPopupPropertyEditor::GenericPopupPropertyEditor(QWidget *pParent, bool showTwoButtons)
:QWidget(pParent)
{
m_valueLabel = new QLabel;
QToolButton *mainButton = new QToolButton;
mainButton->setAutoRaise(true);
mainButton->setIcon(QIcon(QStringLiteral(":/stylesheet/img/UI20/browse-edit.svg")));
connect(mainButton, &QToolButton::clicked, this, &GenericPopupPropertyEditor::onEditClicked);
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->addWidget(m_valueLabel, 1);
mainLayout->addWidget(mainButton);
mainLayout->setContentsMargins(0, 0, 0, 0);
if (showTwoButtons)
{
QToolButton *button2 = new QToolButton;
button2->setAutoRaise(true);
button2->setIcon(QIcon(QStringLiteral(":/stylesheet/img/UI20/more.svg")));
connect(button2, &QToolButton::clicked, this, &GenericPopupPropertyEditor::onButton2Clicked);
mainLayout->insertWidget(1, button2);
}
}
void GenericPopupPropertyEditor::SetValue(const QString &value, bool notify)
{
if (m_value != value)
{
m_value = value;
m_valueLabel->setText(m_value);
if (notify)
emit ValueChanged(m_value);
}
}
void GenericPopupPropertyEditor::SetPropertyType(PropertyType type)
{
m_propertyType = type;
}
void ShaderPropertyEditor::onEditClicked()
{
CShadersDialog cShaders(GetValue());
if (cShaders.exec() == QDialog::Accepted)
{
SetValue(cShaders.GetSelection());
}
}
void MaterialPropertyEditor::onEditClicked()
{
QString name = GetValue();
IDataBaseItem *pItem = GetIEditor()->GetMaterialManager()->FindItemByName(name);
GetIEditor()->OpenMaterialLibrary(pItem);
}
void MaterialPropertyEditor::onButton2Clicked()
{
// Open material browser dialog.
IDataBaseItem *pItem = GetIEditor()->GetMaterialManager()->GetSelectedItem();
if (pItem)
{
QString value = pItem->GetName();
value.replace('\\', '/');
if (value.length() >= MAX_PATH)
value = value.left(MAX_PATH);
SetValue(value);
}
else
SetValue(QString());
}
void ReverbPresetPropertyEditor::onEditClicked()
{
CSelectEAXPresetDlg PresetDlg(this);
PresetDlg.SetCurrPreset(GetValue());
if (PresetDlg.exec() == QDialog::Accepted)
{
SetValue(PresetDlg.GetCurrPreset());
}
}
void SequencePropertyEditor::onEditClicked()
{
CSelectSequenceDialog gtDlg(this);
gtDlg.PreSelectItem(GetValue());
if (gtDlg.exec() == QDialog::Accepted)
SetValue(gtDlg.GetSelectedItem());
}
void SequenceIdPropertyEditor::onEditClicked()
{
CSelectSequenceDialog gtDlg;
uint32 id = GetValue().toUInt();
IAnimSequence *pSeq = GetIEditor()->GetMovieSystem()->FindSequenceById(id);
if (pSeq)
gtDlg.PreSelectItem(pSeq->GetName());
if (gtDlg.exec() == QDialog::Accepted)
{
pSeq = GetIEditor()->GetMovieSystem()->FindLegacySequenceByName(gtDlg.GetSelectedItem().toUtf8().data());
assert(pSeq);
if (pSeq->GetId() > 0)
{
// This sequence is a new one with a valid ID.
SetValue(QString::number(pSeq->GetId()));
}
else
{
// This sequence is an old one without an ID.
QMessageBox::warning(this, tr("Old Sequence"), tr("This is an old sequence without an ID.\nSo it cannot be used with the new ID-based linking."));
}
}
}
void LocalStringPropertyEditor::onEditClicked()
{
std::vector<IVariable::IGetCustomItems::SItem> items;
ILocalizationManager* pMgr = gEnv->pSystem->GetLocalizationManager();
if (!pMgr)
return;
int nCount = pMgr->GetLocalizedStringCount();
if (nCount <= 0)
return;
items.reserve(nCount);
IVariable::IGetCustomItems::SItem item;
SLocalizedInfoEditor sInfo;
for (int i = 0; i < nCount; ++i)
{
if (pMgr->GetLocalizedInfoByIndex(i, sInfo))
{
item.desc = tr("English Text:\r\n");
item.desc += QString::fromWCharArray(Unicode::Convert<wstring>(sInfo.sUtf8TranslatedText).c_str());
item.name = sInfo.sKey;
items.push_back(item);
}
}
CGenericSelectItemDialog gtDlg;
const bool bUseTree = true;
if (bUseTree)
{
gtDlg.SetMode(CGenericSelectItemDialog::eMODE_TREE);
gtDlg.SetTreeSeparator("/");
}
gtDlg.SetItems(items);
gtDlg.setWindowTitle(tr("Choose Localized String"));
QString preselect = GetValue();
if (!preselect.isEmpty() && preselect.at(0) == '@')
preselect = preselect.mid(1);
gtDlg.PreSelectItem(preselect);
if (gtDlg.exec() == QDialog::Accepted)
{
preselect = "@";
preselect += gtDlg.GetSelectedItem();
SetValue(preselect);
}
}
void LightAnimationPropertyEditor::onEditClicked()
{
// First, check if there is any light animation defined.
bool bLightAnimationExists = false;
IMovieSystem *pMovieSystem = GetIEditor()->GetMovieSystem();
for (int i = 0; i < pMovieSystem->GetNumSequences(); ++i)
{
IAnimSequence *pSequence = pMovieSystem->GetSequence(i);
if (pSequence->GetFlags() & IAnimSequence::eSeqFlags_LightAnimationSet)
{
bLightAnimationExists = pSequence->GetNodeCount() > 0;
break;
}
}
if (bLightAnimationExists) // If exists, show the selection dialog.
{
CSelectLightAnimationDialog dlg;
dlg.PreSelectItem(GetValue());
if (dlg.exec() == QDialog::Accepted)
SetValue(dlg.GetSelectedItem());
}
else // If not, remind the user of creating one in TrackView.
{
QMessageBox::warning(this, tr("No Available Animation"), tr("There is no available light animation.\nPlease create one in TrackView, first."));
}
}
ListEditWidget::ListEditWidget(QWidget *pParent /*= nullptr*/)
:QWidget(pParent)
{
m_valueEdit = new QLineEdit;
m_model = new QStringListModel(this);
m_listView = new QListView;
m_listView->setModel(m_model);
m_listView->setMaximumHeight(50);
m_listView->setVisible(false);
QToolButton *expandButton = new QToolButton();
expandButton->setCheckable(true);
expandButton->setText("+");
QToolButton *editButton = new QToolButton();
editButton->setText("..");
connect(editButton, &QAbstractButton::clicked, this, &ListEditWidget::OnEditClicked);
connect(expandButton, &QAbstractButton::toggled, m_listView, &QWidget::setVisible);
connect(m_model, &QAbstractItemModel::dataChanged, this, &ListEditWidget::OnModelDataChange);
connect(m_valueEdit, &QLineEdit::editingFinished, this, [this](){SetValue(m_valueEdit->text(), true); } );
QVBoxLayout *mainLayout = new QVBoxLayout(this);
QHBoxLayout *topLayout = new QHBoxLayout;
topLayout->addWidget(expandButton);
topLayout->addWidget(m_valueEdit,1);
topLayout->addWidget(editButton);
mainLayout->addLayout(topLayout);
mainLayout->addWidget(m_listView,1);
mainLayout->setContentsMargins(1,1,1,1);
}
void ListEditWidget::SetValue(const QString &value, bool notify /*= true*/)
{
if (m_value != value)
{
m_value = value;
m_valueEdit->setText(value);
QStringList list = m_value.split(",", Qt::SkipEmptyParts);
m_model->setStringList(list);
if (notify)
emit ValueChanged(m_value);
}
}
void ListEditWidget::OnModelDataChange()
{
m_value = m_model->stringList().join(",");
m_valueEdit->setText(m_value);
emit ValueChanged(m_value);
}
QWidget* ListEditWidget::GetFirstInTabOrder()
{
return m_valueEdit;
}
QWidget* ListEditWidget::GetLastInTabOrder()
{
return m_listView;
}
#include <Controls/ReflectedPropertyControl/moc_PropertyGenericCtrl.cpp>
@@ -0,0 +1,258 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_EDITOR_UTILS_PROPERTYGENERICCTRL_H
#define CRYINCLUDE_EDITOR_UTILS_PROPERTYGENERICCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include "ReflectedVar.h"
#include "Util/VariablePropertyType.h"
#include <QWidget>
#endif
class QStringListModel;
class QListView;
class QLabel;
class QLineEdit;
class GenericPopupPropertyEditor
: public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(GenericPopupPropertyEditor, AZ::SystemAllocator, 0);
GenericPopupPropertyEditor(QWidget* pParent = nullptr, bool showTwoButtons = false);
void SetValue(const QString& value, bool notify = true);
QString GetValue() const { return m_value; }
void SetPropertyType(PropertyType type);
PropertyType GetPropertyType() const { return m_propertyType; }
//override in derived classes to show appropriate editor
virtual void onEditClicked() {};
virtual void onButton2Clicked() {};
signals:
void ValueChanged(const QString& value);
private:
QLabel* m_valueLabel;
PropertyType m_propertyType;
QString m_value;
};
template <class T, AZ::u32 CRC>
class GenericPopupWidgetHandler
: public QObject
, public AzToolsFramework::PropertyHandler < CReflectedVarGenericProperty, GenericPopupPropertyEditor >
{
public:
AZ_CLASS_ALLOCATOR(GenericPopupWidgetHandler, AZ::SystemAllocator, 0);
virtual bool IsDefaultHandler() const override { return false; }
virtual AZ::u32 GetHandlerName(void) const override { return CRC; }
virtual QWidget* CreateGUI(QWidget* pParent) override
{
GenericPopupPropertyEditor* newCtrl = aznew T(pParent);
connect(newCtrl, &GenericPopupPropertyEditor::ValueChanged, newCtrl, [newCtrl]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl);
});
return newCtrl;
}
virtual void ConsumeAttribute(GenericPopupPropertyEditor* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override
{
Q_UNUSED(GUI);
Q_UNUSED(attrib);
Q_UNUSED(attrValue);
Q_UNUSED(debugName);
}
virtual void WriteGUIValuesIntoProperty(size_t index, GenericPopupPropertyEditor* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarGenericProperty val = instance;
val.m_propertyType = GUI->GetPropertyType();
val.m_value = GUI->GetValue().toUtf8().data();
instance = static_cast<property_t>(val);
}
virtual bool ReadValuesIntoGUI(size_t index, GenericPopupPropertyEditor* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarGenericProperty val = instance;
GUI->SetPropertyType(val.m_propertyType);
GUI->SetValue(val.m_value.c_str(), false);
return false;
}
};
class ShaderPropertyEditor
: public GenericPopupPropertyEditor
{
public:
ShaderPropertyEditor(QWidget* pParent = nullptr)
: GenericPopupPropertyEditor(pParent){}
void onEditClicked() override;
};
class MaterialPropertyEditor
: public GenericPopupPropertyEditor
{
public:
MaterialPropertyEditor(QWidget* pParent = nullptr)
: GenericPopupPropertyEditor(pParent, true){}
void onEditClicked() override;
void onButton2Clicked() override;
};
class ReverbPresetPropertyEditor
: public GenericPopupPropertyEditor
{
public:
ReverbPresetPropertyEditor(QWidget* pParent = nullptr)
: GenericPopupPropertyEditor(pParent){}
void onEditClicked() override;
};
class MissionObjPropertyEditor
: public GenericPopupPropertyEditor
{
public:
MissionObjPropertyEditor(QWidget* pParent = nullptr)
: GenericPopupPropertyEditor(pParent){}
void onEditClicked() override;
};
class SequencePropertyEditor
: public GenericPopupPropertyEditor
{
public:
SequencePropertyEditor(QWidget* pParent = nullptr)
: GenericPopupPropertyEditor(pParent){}
void onEditClicked() override;
};
class SequenceIdPropertyEditor
: public GenericPopupPropertyEditor
{
public:
SequenceIdPropertyEditor(QWidget* pParent = nullptr)
: GenericPopupPropertyEditor(pParent){}
void onEditClicked() override;
};
class LocalStringPropertyEditor
: public GenericPopupPropertyEditor
{
public:
LocalStringPropertyEditor(QWidget* pParent = nullptr)
: GenericPopupPropertyEditor(pParent){}
void onEditClicked() override;
};
class LightAnimationPropertyEditor
: public GenericPopupPropertyEditor
{
public:
LightAnimationPropertyEditor(QWidget* pParent = nullptr)
: GenericPopupPropertyEditor(pParent){}
void onEditClicked() override;
};
// AZ_CRC changed recently - it used to be evaluated by the preprocessor to AZ::u32(value); now it evaluates to Az::Crc32, and can't be used as a const template parameter
// So we use our own
#define CONST_AZ_CRC(name, value) AZ::u32(value)
using ShaderPropertyHandler = GenericPopupWidgetHandler<ShaderPropertyEditor, CONST_AZ_CRC("ePropertyShader", 0xc40932f1)>;
using MaterialPropertyHandler = GenericPopupWidgetHandler<MaterialPropertyEditor, CONST_AZ_CRC("ePropertyMaterial", 0xf324dffa)>;
using ReverbPresetPropertyHandler = GenericPopupWidgetHandler<ReverbPresetPropertyEditor, CONST_AZ_CRC("ePropertyReverbPreset", 0x51469f38)>;
using MissionObjPropertyHandler = GenericPopupWidgetHandler<MissionObjPropertyEditor, CONST_AZ_CRC("ePropertyMissionObj", 0x4a2d0dc8)>;
using SequencePropertyHandler = GenericPopupWidgetHandler<SequencePropertyEditor, CONST_AZ_CRC("ePropertySequence", 0xdd1c7d44)>;
using SequenceIdPropertyHandler = GenericPopupWidgetHandler<SequenceIdPropertyEditor, CONST_AZ_CRC("ePropertySequenceId", 0x05983dcc)>;
using LocalStringPropertyHandler = GenericPopupWidgetHandler<LocalStringPropertyEditor, CONST_AZ_CRC("ePropertyLocalString", 0x0cd9609a)>;
using LightAnimationPropertyHandler = GenericPopupWidgetHandler<LightAnimationPropertyEditor, CONST_AZ_CRC("ePropertyLightAnimation", 0x277097da)>;
class ListEditWidget : public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ListEditWidget, AZ::SystemAllocator, 0);
ListEditWidget(QWidget *pParent = nullptr);
void SetValue(const QString &value, bool notify = true);
QString GetValue() const { return m_value; }
QWidget* GetFirstInTabOrder();
QWidget* GetLastInTabOrder();
signals:
void ValueChanged(const QString &value);
private:
void OnModelDataChange();
virtual void OnEditClicked() {};
protected:
QLineEdit *m_valueEdit;
QString m_value;
QListView *m_listView;
QStringListModel *m_model;
};
template <class T, AZ::u32 CRC>
class ListEditWidgetHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarGenericProperty, ListEditWidget >
{
public:
AZ_CLASS_ALLOCATOR(ListEditWidgetHandler, AZ::SystemAllocator, 0);
virtual bool IsDefaultHandler() const override { return false; }
virtual AZ::u32 GetHandlerName(void) const override { return CRC; }
virtual QWidget* CreateGUI(QWidget *pParent) override
{
ListEditWidget* newCtrl = aznew T(pParent);
connect(newCtrl, &ListEditWidget::ValueChanged, newCtrl, [newCtrl]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl);
});
return newCtrl;
}
virtual void ConsumeAttribute(ListEditWidget* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override {
Q_UNUSED(GUI); Q_UNUSED(attrib); Q_UNUSED(attrValue); Q_UNUSED(debugName);
}
virtual void WriteGUIValuesIntoProperty(size_t index, ListEditWidget* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarGenericProperty val = instance;
val.m_value = GUI->GetValue().toUtf8().data();
instance = static_cast<property_t>(val);
}
virtual bool ReadValuesIntoGUI(size_t index, ListEditWidget* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarGenericProperty val = instance;
GUI->SetValue(val.m_value.c_str(), false);
return false;
}
QWidget* GetFirstInTabOrder(ListEditWidget* widget) override { return widget->GetFirstInTabOrder(); }
QWidget* GetLastInTabOrder(ListEditWidget* widget) override {return widget->GetLastInTabOrder(); }
};
#endif // CRYINCLUDE_EDITOR_UTILS_PROPERTYGENERICCTRL_H
@@ -0,0 +1,277 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 "EditorDefs.h"
#include "PropertyMiscCtrl.h"
// Qt
#include <QHBoxLayout>
#include <QtWidgets/QLabel>
#include <QLineEdit>
#include <QtWidgets/QToolButton>
#include <QtCore/QTimer>
#include <QtUtilWin.h>
// Editor
#include "GenericSelectItemDialog.h"
#include "QtViewPaneManager.h"
#include "LensFlareEditor/LensFlareEditor.h"
UserPropertyEditor::UserPropertyEditor(QWidget *pParent /*= nullptr*/)
: QWidget(pParent)
, m_canEdit(false)
, m_useTree(false)
{
m_valueLabel = new QLabel;
QToolButton *mainButton = new QToolButton;
mainButton->setText("..");
connect(mainButton, &QToolButton::clicked, this, &UserPropertyEditor::onEditClicked);
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->addWidget(m_valueLabel, 1);
mainLayout->addWidget(mainButton);
mainLayout->setContentsMargins(1, 1, 1, 1);
}
void UserPropertyEditor::SetValue(const QString &value, bool notify /*= true*/)
{
if (m_value != value)
{
m_value = value;
m_valueLabel->setText(m_value);
if (notify)
{
emit ValueChanged(m_value);
}
}
}
void UserPropertyEditor::SetData(bool canEdit, bool useTree, const QString &treeSeparator, const QString &dialogTitle, const std::vector<IVariable::IGetCustomItems::SItem>& items)
{
m_canEdit = canEdit;
m_useTree = useTree;
m_treeSeparator = treeSeparator;
m_dialogTitle = dialogTitle;
m_items = items;
}
void UserPropertyEditor::onEditClicked()
{
// call the user supplied callback to fill-in items and get dialog title
emit RefreshItems();
if (m_canEdit) // if func didn't veto, show the dialog
{
CGenericSelectItemDialog gtDlg;
if (m_useTree)
{
gtDlg.SetMode(CGenericSelectItemDialog::eMODE_TREE);
if (!m_treeSeparator.isEmpty())
{
gtDlg.SetTreeSeparator(m_treeSeparator);
}
}
gtDlg.SetItems(m_items);
if (m_dialogTitle.isEmpty() == false)
gtDlg.setWindowTitle(m_dialogTitle);
gtDlg.PreSelectItem(GetValue());
if (gtDlg.exec() == QDialog::Accepted)
{
QString selectedItemStr = gtDlg.GetSelectedItem();
if (selectedItemStr.isEmpty() == false)
{
SetValue(selectedItemStr);
}
}
}
}
QWidget* UserPopupWidgetHandler::CreateGUI(QWidget *pParent)
{
UserPropertyEditor* newCtrl = aznew UserPropertyEditor(pParent);
connect(newCtrl, &UserPropertyEditor::ValueChanged, newCtrl, [newCtrl]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl);
});
return newCtrl;
}
void UserPopupWidgetHandler::ConsumeAttribute(UserPropertyEditor* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
Q_UNUSED(GUI);
Q_UNUSED(attrib);
Q_UNUSED(attrValue);
Q_UNUSED(debugName);
}
void UserPopupWidgetHandler::WriteGUIValuesIntoProperty(size_t index, UserPropertyEditor* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node)
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarUser val = instance;
val.m_value = GUI->GetValue().toUtf8().data();
instance = static_cast<property_t>(val);
}
bool UserPopupWidgetHandler::ReadValuesIntoGUI(size_t index, UserPropertyEditor* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node)
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarUser val = instance;
assert(val.m_itemNames.size() == val.m_itemDescriptions.size());
std::vector<IVariable::IGetCustomItems::SItem> items(val.m_itemNames.size());
int i = -1;
std::generate(items.begin(), items.end(), [&val, &i]() { ++i; return IVariable::IGetCustomItems::SItem(val.m_itemNames[i].c_str(), val.m_itemDescriptions[i].c_str());});
GUI->SetData(val.m_enableEdit, val.m_useTree, val.m_treeSeparator.c_str(), val.m_dialogTitle.c_str(), items);
GUI->SetValue(val.m_value.c_str(), false);
return false;
}
#include <Controls/ReflectedPropertyControl/moc_PropertyMiscCtrl.cpp>
LensFlarePropertyWidget::LensFlarePropertyWidget(QWidget *pParent /*= nullptr*/)
:QWidget(pParent)
{
m_valueEdit = new QLineEdit;
QToolButton *mainButton = new QToolButton;
mainButton->setText("D");
connect(mainButton, &QToolButton::clicked, this, &LensFlarePropertyWidget::OnEditClicked);
connect(m_valueEdit, &QLineEdit::editingFinished, m_valueEdit, [this] () {emit ValueChanged(m_valueEdit->text());});
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->addWidget(m_valueEdit, 1);
mainLayout->addWidget(mainButton);
mainLayout->setContentsMargins(1, 1, 1, 1);
}
void LensFlarePropertyWidget::SetValue(const QString &value)
{
m_valueEdit->setText(value);
}
QString LensFlarePropertyWidget::GetValue() const
{
return m_valueEdit->text();
}
void LensFlarePropertyWidget::OnEditClicked()
{
const QtViewPane *lensFlarePane = GetIEditor()->OpenView(CLensFlareEditor::s_pLensFlareEditorClassName);
if (!lensFlarePane)
return;
CLensFlareEditor *editor = FindViewPane<CLensFlareEditor>(QtUtil::ToQString(CLensFlareEditor::s_pLensFlareEditorClassName));
if (editor)
QTimer::singleShot(0, editor, SLOT(OnUpdateTreeCtrl()));
}
QWidget* LensFlareHandler::CreateGUI(QWidget *pParent)
{
LensFlarePropertyWidget* newCtrl = aznew LensFlarePropertyWidget(pParent);
connect(newCtrl, &LensFlarePropertyWidget::ValueChanged, newCtrl, [newCtrl]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl);
});
return newCtrl;
}
void LensFlareHandler::ConsumeAttribute(LensFlarePropertyWidget* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
Q_UNUSED(GUI); Q_UNUSED(attrib); Q_UNUSED(attrValue); Q_UNUSED(debugName);
}
void LensFlareHandler::WriteGUIValuesIntoProperty(size_t index, LensFlarePropertyWidget* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node)
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarGenericProperty val = instance;
val.m_value = GUI->GetValue().toUtf8().data();
instance = static_cast<property_t>(val);
}
bool LensFlareHandler::ReadValuesIntoGUI(size_t index, LensFlarePropertyWidget* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node)
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarGenericProperty val = instance;
GUI->SetValue(val.m_value.c_str());
return false;
}
QWidget* FloatCurveHandler::CreateGUI(QWidget *pParent)
{
CSplineCtrl *cSpline = new CSplineCtrl(pParent);
cSpline->SetUpdateCallback(functor(*this, &FloatCurveHandler::OnSplineChange));
cSpline->SetTimeRange(0, 1);
cSpline->SetValueRange(0, 1);
cSpline->SetGrid(12, 12);
cSpline->setFixedHeight(52);
return cSpline;
}
void FloatCurveHandler::OnSplineChange(CSplineCtrl*)
{
// EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, splineWidget);
}
void FloatCurveHandler::ConsumeAttribute(CSplineCtrl *, AZ::u32, AzToolsFramework::PropertyAttributeReader*, const char*)
{}
void FloatCurveHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, [[maybe_unused]] CSplineCtrl* GUI, [[maybe_unused]] property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
//nothing to do here. the spline itself will have it's new values.
}
bool FloatCurveHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, CSplineCtrl* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
GUI->SetSpline(reinterpret_cast<ISplineInterpolator*>(instance.m_spline));
return false;
}
QWidget* ColorCurveHandler::CreateGUI(QWidget *pParent)
{
CColorGradientCtrl* gradientCtrl = new CColorGradientCtrl(pParent);
//connect(gradientCtrl, &CColorGradientCtrl::change, [gradientCtrl]()
//{
// EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, gradientCtrl);
//});
gradientCtrl->SetTimeRange(0, 1);
gradientCtrl->setFixedHeight(36);
return gradientCtrl;
}
void ColorCurveHandler::ConsumeAttribute(CColorGradientCtrl*, AZ::u32, AzToolsFramework::PropertyAttributeReader*, const char*)
{}
void ColorCurveHandler::WriteGUIValuesIntoProperty([[maybe_unused]] size_t index, [[maybe_unused]] CColorGradientCtrl* GUI, [[maybe_unused]] property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{}
bool ColorCurveHandler::ReadValuesIntoGUI([[maybe_unused]] size_t index, CColorGradientCtrl* GUI, const property_t& instance, [[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
{
GUI->SetSpline(reinterpret_cast<ISplineInterpolator*>(instance.m_spline));
return false;
}
@@ -0,0 +1,135 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H
#define CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include "ReflectedVar.h"
#include "Util/VariablePropertyType.h"
#include "Controls/ColorGradientCtrl.h"
#include "Controls/SplineCtrl.h"
#include <QWidget>
#endif
class QLabel;
class QLineEdit;
class UserPropertyEditor : public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(UserPropertyEditor, AZ::SystemAllocator, 0);
UserPropertyEditor(QWidget *pParent = nullptr);
void SetValue(const QString &value, bool notify = true);
QString GetValue() const { return m_value; }
void SetData(bool canEdit, bool useTree, const QString &treeSeparator, const QString &dialogTitle, const std::vector<IVariable::IGetCustomItems::SItem>& items);
void onEditClicked();
signals:
void ValueChanged(const QString &value);
void RefreshItems();
private:
QLabel *m_valueLabel;
QString m_value;
bool m_canEdit;
bool m_useTree;
QString m_treeSeparator;
QString m_dialogTitle;
std::vector<IVariable::IGetCustomItems::SItem> m_items;
};
class UserPopupWidgetHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarUser, UserPropertyEditor>
{
public:
AZ_CLASS_ALLOCATOR(UserPopupWidgetHandler, AZ::SystemAllocator, 0);
bool IsDefaultHandler() const override { return false; }
QWidget* CreateGUI(QWidget *pParent) override;
AZ::u32 GetHandlerName(void) const override {return AZ_CRC("ePropertyUser", 0x65b972c0); }
void ConsumeAttribute(UserPropertyEditor* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, UserPropertyEditor* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, UserPropertyEditor* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
};
class LensFlarePropertyWidget : public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(LensFlarePropertyWidget, AZ::SystemAllocator, 0);
LensFlarePropertyWidget(QWidget *pParent = nullptr);
void SetValue(const QString &value);
QString GetValue() const;
void OnEditClicked();
signals:
void ValueChanged(const QString &value);
private:
QLineEdit *m_valueEdit;
};
class LensFlareHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarGenericProperty, LensFlarePropertyWidget>
{
public:
AZ_CLASS_ALLOCATOR(LensFlareHandler, AZ::SystemAllocator, 0);
bool IsDefaultHandler() const override { return false; }
QWidget* CreateGUI(QWidget *pParent) override;
AZ::u32 GetHandlerName(void) const override { return AZ_CRC("ePropertyFlare", 0x5ce803df); }
void ConsumeAttribute(LensFlarePropertyWidget* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, LensFlarePropertyWidget* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, LensFlarePropertyWidget* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
};
class FloatCurveHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarSpline, CSplineCtrl>
{
public:
AZ_CLASS_ALLOCATOR(FloatCurveHandler, AZ::SystemAllocator, 0);
bool IsDefaultHandler() const override { return false; }
QWidget* CreateGUI(QWidget *pParent) override;
AZ::u32 GetHandlerName(void) const override { return AZ_CRC("ePropertyFloatCurve", 0x7440ccce); }
void ConsumeAttribute(CSplineCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, CSplineCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, CSplineCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
void OnSplineChange(CSplineCtrl*);
};
class ColorCurveHandler : public QObject, public AzToolsFramework::PropertyHandler < CReflectedVarSpline, CColorGradientCtrl>
{
public:
AZ_CLASS_ALLOCATOR(ColorCurveHandler, AZ::SystemAllocator, 0);
bool IsDefaultHandler() const override { return false; }
QWidget* CreateGUI(QWidget *pParent) override;
AZ::u32 GetHandlerName(void) const override { return AZ_CRC("ePropertyColorCurve", 0xa30da4ec); }
void ConsumeAttribute(CColorGradientCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, CColorGradientCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, CColorGradientCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
};
#endif // CRYINCLUDE_EDITOR_UTILS_PROPERTYMISCCTRL_H
@@ -0,0 +1,187 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 "EditorDefs.h"
#include "PropertyMotionCtrl.h"
// Qt
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
// AzToolsFramework
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
MotionPropertyCtrl::MotionPropertyCtrl(QWidget *pParent)
: QWidget(pParent)
{
m_motionLabel = new QLabel;
m_pBrowseButton = new QToolButton;
m_pBrowseButton->setIcon(QIcon(":/reflectedPropertyCtrl/img/file_browse.png"));
m_pApplyButton = new QToolButton;
m_pApplyButton->setIcon(QIcon(":/reflectedPropertyCtrl/img/apply.png"));
m_pApplyButton->setFocusPolicy(Qt::StrongFocus);
m_pBrowseButton->setFocusPolicy(Qt::StrongFocus);
QHBoxLayout *pLayout = new QHBoxLayout(this);
pLayout->setContentsMargins(0, 0, 0, 0);
pLayout->addWidget(m_motionLabel, 1);
pLayout->addWidget(m_pBrowseButton);
pLayout->addWidget(m_pApplyButton);
connect(m_pBrowseButton, &QAbstractButton::clicked, this, &MotionPropertyCtrl::OnBrowseClicked);
connect(m_pApplyButton, &QAbstractButton::clicked, this, &MotionPropertyCtrl::OnApplyClicked);
};
MotionPropertyCtrl::~MotionPropertyCtrl()
{
}
void MotionPropertyCtrl::SetValue(const CReflectedVarMotion &motion)
{
m_motion = motion;
SetLabelText(motion.m_motion);
}
CReflectedVarMotion MotionPropertyCtrl::value() const
{
return m_motion;
}
void MotionPropertyCtrl::OnBrowseClicked()
{
static AZ::Data::AssetType emotionFXMotionAssetType("{00494B8E-7578-4BA2-8B28-272E90680787}"); // from MotionAsset.h in EMotionFX Gem
// Request the AssetBrowser Dialog and set a type filter
AssetSelectionModel selection = AssetSelectionModel::AssetTypeSelection(emotionFXMotionAssetType);
selection.SetSelectedAssetId(m_motion.m_assetId);
AzToolsFramework::EditorRequests::Bus::Broadcast(&AzToolsFramework::EditorRequests::BrowseForAssets, selection);
if (selection.IsValid())
{
auto product = azrtti_cast<const ProductAssetBrowserEntry*>(selection.GetResult());
if (product != nullptr)
{
m_motion.m_motion = product->GetRelativePath();
m_motion.m_assetId = product->GetAssetId();
SetLabelText(m_motion.m_motion);
emit ValueChanged(m_motion);
}
}
}
// TODO: Might be able to delete this function
void MotionPropertyCtrl::OnApplyClicked()
{
#if 0
CUIEnumerations &roGeneralProxy = CUIEnumerations::GetUIEnumerationsInstance();
QStringList cSelectedMotions;
size_t nTotalMotions(0);
size_t nCurrentMotion(0);
QString combinedString = GetIEditor()->GetResourceSelectorHost()->GetGlobalSelection("motion");
SplitString(combinedString, cSelectedMotions, ',');
nTotalMotions = cSelectedMotions.size();
for (nCurrentMotion = 0; nCurrentMotion < nTotalMotions; ++nCurrentMotion)
{
QString& rstrCurrentAnimAction = cSelectedMotions[nCurrentMotion];
if (!rstrCurrentAnimAction.isEmpty())
{
m_motion.m_motion = rstrCurrentAnimAction.toLatin1().data();
SetLabelText(m_motion.m_motion);
emit ValueChanged(m_motion);
}
}
#endif
}
QWidget* MotionPropertyCtrl::GetFirstInTabOrder()
{
return m_pBrowseButton;
}
QWidget* MotionPropertyCtrl::GetLastInTabOrder()
{
return m_pApplyButton;
}
void MotionPropertyCtrl::UpdateTabOrder()
{
setTabOrder(m_pBrowseButton, m_pApplyButton);
}
void MotionPropertyCtrl::SetLabelText(const AZStd::string& motion)
{
if (!motion.empty())
{
AZStd::string filename;
if (AzFramework::StringFunc::Path::GetFileName(motion.c_str(), filename))
{
m_motionLabel->setText(filename.c_str());
}
else
{
m_motionLabel->setText(motion.c_str());
}
}
else
{
m_motionLabel->setText("");
}
}
QWidget* MotionPropertyWidgetHandler::CreateGUI(QWidget *pParent)
{
MotionPropertyCtrl* newCtrl = aznew MotionPropertyCtrl(pParent);
connect(newCtrl, &MotionPropertyCtrl::ValueChanged, newCtrl, [newCtrl]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl);
});
return newCtrl;
}
void MotionPropertyWidgetHandler::ConsumeAttribute(MotionPropertyCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
Q_UNUSED(GUI);
Q_UNUSED(attrib);
Q_UNUSED(attrValue);
Q_UNUSED(debugName);
}
void MotionPropertyWidgetHandler::WriteGUIValuesIntoProperty(size_t index, MotionPropertyCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node)
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarMotion val = GUI->value();
instance = static_cast<property_t>(val);
}
bool MotionPropertyWidgetHandler::ReadValuesIntoGUI(size_t index, MotionPropertyCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node)
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarMotion val = instance;
GUI->SetValue(val);
return false;
}
#include <Controls/ReflectedPropertyControl/moc_PropertyMotionCtrl.cpp>
@@ -0,0 +1,92 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_EDITOR_UTILS_PROPERTYMOTIONCTRL_H
#define CRYINCLUDE_EDITOR_UTILS_PROPERTYMOTIONCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include "ReflectedVar.h"
#include <QWidget>
#include <QPointer>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#endif
class QToolButton;
class QLabel;
class QHBoxLayout;
namespace AzToolsFramework
{
class PropertyAssetCtrl;
}
class MotionPropertyCtrl
: public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(MotionPropertyCtrl, AZ::SystemAllocator, 0);
MotionPropertyCtrl(QWidget* pParent = nullptr);
virtual ~MotionPropertyCtrl();
CReflectedVarMotion value() const;
QWidget* GetFirstInTabOrder();
QWidget* GetLastInTabOrder();
void UpdateTabOrder();
signals:
void ValueChanged(CReflectedVarMotion value);
public slots:
void SetValue(const CReflectedVarMotion& motion);
protected slots:
void OnBrowseClicked();
void OnApplyClicked();
private:
void SetLabelText(const AZStd::string& motion);
QToolButton* m_pBrowseButton;
QToolButton* m_pApplyButton;
QLabel* m_motionLabel;
CReflectedVarMotion m_motion;
};
class MotionPropertyWidgetHandler
: QObject
, public AzToolsFramework::PropertyHandler < CReflectedVarMotion, MotionPropertyCtrl >
{
public:
AZ_CLASS_ALLOCATOR(MotionPropertyWidgetHandler, AZ::SystemAllocator, 0);
virtual AZ::u32 GetHandlerName(void) const override { return AZ_CRC("Motion", 0xf5fea1e8); }
virtual bool IsDefaultHandler() const override { return true; }
virtual QWidget* GetFirstInTabOrder(MotionPropertyCtrl* widget) override { return widget->GetFirstInTabOrder(); }
virtual QWidget* GetLastInTabOrder(MotionPropertyCtrl* widget) override { return widget->GetLastInTabOrder(); }
virtual void UpdateWidgetInternalTabbing(MotionPropertyCtrl* widget) override { widget->UpdateTabOrder(); }
virtual QWidget* CreateGUI(QWidget* pParent) override;
virtual void ConsumeAttribute(MotionPropertyCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
virtual void WriteGUIValuesIntoProperty(size_t index, MotionPropertyCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
virtual bool ReadValuesIntoGUI(size_t index, MotionPropertyCtrl* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
};
#endif // CRYINCLUDE_EDITOR_UTILS_PROPERTYMOTIONCTRL_H
@@ -0,0 +1,399 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 "EditorDefs.h"
#include "PropertyResourceCtrl.h"
// Qt
#include <QHBoxLayout>
#include <QLineEdit>
// AzToolsFramework
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
// Editor
#include "IResourceSelectorHost.h"
#include "Controls/QToolTipWidget.h"
#include "Controls/BitmapToolTip.h"
BrowseButton::BrowseButton(PropertyType type, QWidget* parent /*= nullptr*/)
: QToolButton(parent)
, m_propertyType(type)
{
setAutoRaise(true);
setIcon(QIcon(QStringLiteral(":/stylesheet/img/UI20/browse-edit.svg")));
connect(this, &QAbstractButton::clicked, this, &BrowseButton::OnClicked);
}
void BrowseButton::SetPathAndEmit(const QString& path)
{
//only emit if path changes, except for ePropertyGeomCache. Old property control
if (path != m_path || m_propertyType == ePropertyGeomCache)
{
m_path = path;
emit PathChanged(m_path);
}
}
class FileBrowseButton
: public BrowseButton
{
public:
AZ_CLASS_ALLOCATOR(FileBrowseButton, AZ::SystemAllocator, 0);
FileBrowseButton(PropertyType type, QWidget* pParent = nullptr)
: BrowseButton(type, pParent)
{
setToolTip("Browse...");
}
private:
void OnClicked() override
{
QString tempValue("");
QString ext("");
if (m_path.isEmpty() == false)
{
if (Path::GetExt(m_path) == "")
{
tempValue = "";
}
else
{
tempValue = m_path;
}
}
AssetSelectionModel selection;
if (m_propertyType == ePropertyTexture)
{
// Filters for texture.
selection = AssetSelectionModel::AssetGroupSelection("Texture");
}
else if (m_propertyType == ePropertyModel)
{
// Filters for models.
selection = AssetSelectionModel::AssetGroupSelection("Geometry");
}
else if (m_propertyType == ePropertyGeomCache)
{
// Filters for geom caches.
selection = AssetSelectionModel::AssetTypeSelection("Geom Cache");
}
else if (m_propertyType == ePropertyFile)
{
// Filters for files.
selection = AssetSelectionModel::AssetTypeSelection("File");
}
else
{
return;
}
AzToolsFramework::EditorRequests::Bus::Broadcast(&AzToolsFramework::EditorRequests::BrowseForAssets, selection);
if (selection.IsValid())
{
QString newPath = Path::FullPathToGamePath(selection.GetResult()->GetFullPath().c_str()).c_str();
switch (m_propertyType)
{
case ePropertyTexture:
case ePropertyModel:
case ePropertyMaterial:
newPath.replace("\\\\", "/");
}
switch (m_propertyType)
{
case ePropertyTexture:
case ePropertyModel:
case ePropertyMaterial:
case ePropertyFile:
if (newPath.size() > MAX_PATH)
{
newPath.resize(MAX_PATH);
}
}
SetPathAndEmit(newPath);
}
}
};
class ResourceSelectorButton
: public BrowseButton
{
public:
AZ_CLASS_ALLOCATOR(ResourceSelectorButton, AZ::SystemAllocator, 0);
ResourceSelectorButton(PropertyType type, QWidget* pParent = nullptr)
: BrowseButton(type, pParent)
{
setToolTip(tr("Select resource"));
}
private:
void OnClicked() override
{
SResourceSelectorContext x;
x.parentWidget = this;
x.typeName = Prop::GetPropertyTypeToResourceType(m_propertyType);
QString newPath = GetIEditor()->GetResourceSelectorHost()->SelectResource(x, m_path);
SetPathAndEmit(newPath);
}
};
class TextureEditButton
: public BrowseButton
{
public:
AZ_CLASS_ALLOCATOR(TextureEditButton, AZ::SystemAllocator, 0);
TextureEditButton(QWidget* pParent = nullptr)
: BrowseButton(ePropertyTexture, pParent)
{
setIcon(QIcon(QStringLiteral(":/stylesheet/img/UI20/open-in-internal-app.svg")));
setToolTip(tr("Launch default editor"));
}
private:
void OnClicked() override
{
CFileUtil::EditTextureFile(m_path.toUtf8().data(), true);
}
};
FileResourceSelectorWidget::FileResourceSelectorWidget(QWidget* pParent /*= nullptr*/)
: QWidget(pParent)
, m_propertyType(ePropertyInvalid)
, m_tooltip(nullptr)
{
m_pathEdit = new QLineEdit;
m_mainLayout = new QHBoxLayout(this);
m_mainLayout->addWidget(m_pathEdit, 1);
m_mainLayout->setContentsMargins(0, 0, 0, 0);
// KDAB just ported the MFC texture preview tooltip, but looks like Amazon added their own. Not sure which to use.
// To switch to Amazon QToolTipWidget, remove FileResourceSelectorWidget::event and m_previewTooltip
#ifdef USE_QTOOLTIPWIDGET
m_tooltip = new QToolTipWidget(this);
installEventFilter(this);
#endif
connect(m_pathEdit, &QLineEdit::editingFinished, this, [this]() { OnPathChanged(m_pathEdit->text()); });
}
bool FileResourceSelectorWidget::eventFilter([[maybe_unused]] QObject* obj, QEvent* event)
{
if (m_propertyType == ePropertyTexture)
{
if (event->type() == QEvent::ToolTip)
{
QHelpEvent* e = (QHelpEvent*)event;
m_tooltip->AddSpecialContent("TEXTURE", m_path);
m_tooltip->TryDisplay(e->globalPos(), m_pathEdit, QToolTipWidget::ArrowDirection::ARROW_RIGHT);
return true;
}
if (event->type() == QEvent::Leave)
{
m_tooltip->hide();
}
}
return false;
}
void FileResourceSelectorWidget::SetPropertyType(PropertyType type)
{
if (m_propertyType == type)
{
return;
}
//if the property type changed for some reason, delete all the existing widgets
if (!m_buttons.isEmpty())
{
qDeleteAll(m_buttons.begin(), m_buttons.end());
m_buttons.clear();
}
m_previewToolTip.reset();
m_propertyType = type;
switch (type)
{
case ePropertyTexture:
AddButton(new FileBrowseButton(type));
AddButton(new TextureEditButton);
m_previewToolTip.reset(new CBitmapToolTip);
break;
case ePropertyModel:
case ePropertyGeomCache:
case ePropertyAudioTrigger:
case ePropertyAudioSwitch:
case ePropertyAudioSwitchState:
case ePropertyAudioRTPC:
case ePropertyAudioEnvironment:
case ePropertyAudioPreloadRequest:
AddButton(new ResourceSelectorButton(type));
break;
case ePropertyFile:
AddButton(new FileBrowseButton(type));
break;
default:
break;
}
m_mainLayout->invalidate();
}
void FileResourceSelectorWidget::AddButton(BrowseButton* button)
{
m_mainLayout->addWidget(button);
m_buttons.push_back(button);
connect(button, &BrowseButton::PathChanged, this, &FileResourceSelectorWidget::OnPathChanged);
}
void FileResourceSelectorWidget::OnPathChanged(const QString& path)
{
bool changed = SetPath(path);
if (changed)
{
emit PathChanged(m_path);
}
}
bool FileResourceSelectorWidget::SetPath(const QString& path)
{
bool changed = false;
const QString newPath = path.toLower();
if (m_path != newPath)
{
m_path = newPath;
UpdateWidgets();
changed = true;
}
return changed;
}
void FileResourceSelectorWidget::UpdateWidgets()
{
m_pathEdit->setText(m_path);
foreach(BrowseButton * button, m_buttons)
{
button->SetPath(m_path);
}
if (m_previewToolTip)
{
m_previewToolTip->SetTool(this, rect());
}
}
QString FileResourceSelectorWidget::GetPath() const
{
return m_path;
}
QWidget* FileResourceSelectorWidget::GetLastInTabOrder()
{
return m_buttons.empty() ? nullptr : m_buttons.last();
}
QWidget* FileResourceSelectorWidget::GetFirstInTabOrder()
{
return m_buttons.empty() ? nullptr : m_buttons.first();
}
void FileResourceSelectorWidget::UpdateTabOrder()
{
if (m_buttons.count() >= 2)
{
for (int i = 0; i < m_buttons.count() - 1; ++i)
{
setTabOrder(m_buttons[i], m_buttons[i + 1]);
}
}
}
bool FileResourceSelectorWidget::event(QEvent* event)
{
if (event->type() == QEvent::ToolTip && m_previewToolTip && !m_previewToolTip->isVisible())
{
if (!m_path.isEmpty())
{
m_previewToolTip->LoadImage(m_path);
m_previewToolTip->setVisible(true);
}
event->accept();
return true;
}
if (event->type() == QEvent::Resize && m_previewToolTip)
{
m_previewToolTip->SetTool(this, rect());
}
return QWidget::event(event);
}
QWidget* FileResourceSelectorWidgetHandler::CreateGUI(QWidget* pParent)
{
FileResourceSelectorWidget* newCtrl = aznew FileResourceSelectorWidget(pParent);
connect(newCtrl, &FileResourceSelectorWidget::PathChanged, newCtrl, [newCtrl]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl);
});
return newCtrl;
}
void FileResourceSelectorWidgetHandler::ConsumeAttribute(FileResourceSelectorWidget* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
Q_UNUSED(GUI);
Q_UNUSED(attrib);
Q_UNUSED(attrValue);
Q_UNUSED(debugName);
}
void FileResourceSelectorWidgetHandler::WriteGUIValuesIntoProperty(size_t index, FileResourceSelectorWidget* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node)
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarResource val = instance;
val.m_propertyType = GUI->GetPropertyType();
val.m_path = GUI->GetPath().toUtf8().data();
instance = static_cast<property_t>(val);
}
bool FileResourceSelectorWidgetHandler::ReadValuesIntoGUI(size_t index, FileResourceSelectorWidget* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node)
{
Q_UNUSED(index);
Q_UNUSED(node);
CReflectedVarResource val = instance;
GUI->SetPropertyType(val.m_propertyType);
GUI->SetPath(val.m_path.c_str());
return false;
}
#include <Controls/ReflectedPropertyControl/moc_PropertyResourceCtrl.cpp>
@@ -0,0 +1,121 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_EDITOR_UTILS_PROPERTYRESOURCECTRL_H
#define CRYINCLUDE_EDITOR_UTILS_PROPERTYRESOURCECTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/base.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include "ReflectedVar.h"
#include "Util/VariablePropertyType.h"
#include <QWidget>
#include <QtWidgets/QToolButton>
#include <QtCore/QVector>
#endif
class QLineEdit;
class QHBoxLayout;
class CBitmapToolTip;
class QToolTipWidget;
class BrowseButton
: public QToolButton
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(BrowseButton, AZ::SystemAllocator, 0);
BrowseButton(PropertyType type, QWidget* parent = nullptr);
void SetPath(const QString& path) { m_path = path; }
QString GetPath() const { return m_path; }
PropertyType GetPropertyType() const {return m_propertyType; }
signals:
void PathChanged(const QString& path);
protected:
void SetPathAndEmit(const QString& path);
virtual void OnClicked() = 0;
PropertyType m_propertyType;
QString m_path;
};
class FileResourceSelectorWidget
: public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(FileResourceSelectorWidget, AZ::SystemAllocator, 0);
FileResourceSelectorWidget(QWidget* pParent = nullptr);
bool SetPath(const QString& path);
QString GetPath() const;
void SetPropertyType(PropertyType type);
PropertyType GetPropertyType() const { return m_propertyType; }
QWidget* GetFirstInTabOrder();
QWidget* GetLastInTabOrder();
void UpdateTabOrder();
bool eventFilter(QObject* obj, QEvent* event) override;
signals:
void PathChanged(const QString& path);
protected:
bool event(QEvent* event) override;
private:
void OnAssignClicked();
void OnMaterialClicked();
void UpdateWidgets();
void AddButton(BrowseButton* button);
void OnPathChanged(const QString& path);
private:
QLineEdit* m_pathEdit;
PropertyType m_propertyType;
QString m_path;
QHBoxLayout* m_mainLayout;
QVector<BrowseButton*> m_buttons;
QScopedPointer<CBitmapToolTip> m_previewToolTip;
QToolTipWidget* m_tooltip;
};
class FileResourceSelectorWidgetHandler
: QObject
, public AzToolsFramework::PropertyHandler < CReflectedVarResource, FileResourceSelectorWidget >
{
public:
AZ_CLASS_ALLOCATOR(FileResourceSelectorWidgetHandler, AZ::SystemAllocator, 0);
virtual AZ::u32 GetHandlerName(void) const override { return AZ_CRC("Resource", 0xbc91f416); }
virtual bool IsDefaultHandler() const override { return true; }
virtual QWidget* GetFirstInTabOrder(FileResourceSelectorWidget* widget) override { return widget->GetFirstInTabOrder(); }
virtual QWidget* GetLastInTabOrder(FileResourceSelectorWidget* widget) override { return widget->GetLastInTabOrder(); }
virtual void UpdateWidgetInternalTabbing(FileResourceSelectorWidget* widget) override { widget->UpdateTabOrder(); }
virtual QWidget* CreateGUI(QWidget* pParent) override;
virtual void ConsumeAttribute(FileResourceSelectorWidget* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
virtual void WriteGUIValuesIntoProperty(size_t index, FileResourceSelectorWidget* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
virtual bool ReadValuesIntoGUI(size_t index, FileResourceSelectorWidget* GUI, const property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
};
#endif // CRYINCLUDE_EDITOR_UTILS_PROPERTYRESOURCECTRL_H
@@ -0,0 +1,97 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : implementation file
#include "EditorDefs.h"
#include "ReflectedPropertiesPanel.h"
/////////////////////////////////////////////////////////////////////////////
// ReflectedPropertiesPanel dialog
ReflectedPropertiesPanel::ReflectedPropertiesPanel(QWidget* pParent)
: ReflectedPropertyControl(pParent)
{
}
//////////////////////////////////////////////////////////////////////////
void ReflectedPropertiesPanel::DeleteVars()
{
ClearVarBlock();
m_updateCallbacks.clear();
m_varBlock = 0;
}
//////////////////////////////////////////////////////////////////////////
void ReflectedPropertiesPanel::SetVarBlock(class CVarBlock* vb, const ReflectedPropertyControl::UpdateVarCallback& updCallback, const char* category)
{
assert(vb);
m_varBlock = vb;
RemoveAllItems();
m_varBlock = vb;
AddVarBlock(m_varBlock, category);
SetUpdateCallback(functor(*this, &ReflectedPropertiesPanel::OnPropertyChanged));
// When new object set all previous callbacks freed.
m_updateCallbacks.clear();
if (updCallback)
{
stl::push_back_unique(m_updateCallbacks, updCallback);
}
}
//////////////////////////////////////////////////////////////////////////
void ReflectedPropertiesPanel::AddVars(CVarBlock* vb, const ReflectedPropertyControl::UpdateVarCallback& updCallback, const char* category)
{
assert(vb);
bool bNewBlock = false;
// Make a clone of properties.
if (!m_varBlock)
{
RemoveAllItems();
m_varBlock = vb->Clone(true);
AddVarBlock(m_varBlock, category);
bNewBlock = true;
}
m_varBlock->Wire(vb);
if (bNewBlock)
{
SetUpdateCallback(functor(*this, &ReflectedPropertiesPanel::OnPropertyChanged));
// When new object set all previous callbacks freed.
m_updateCallbacks.clear();
}
if (updCallback)
{
stl::push_back_unique(m_updateCallbacks, updCallback);
}
}
void ReflectedPropertiesPanel::OnPropertyChanged(IVariable* pVar)
{
std::list<ReflectedPropertyControl::UpdateVarCallback>::iterator iter;
for (iter = m_updateCallbacks.begin(); iter != m_updateCallbacks.end(); ++iter)
{
(*iter)(pVar);
}
}
@@ -0,0 +1,50 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_EDITOR_REFLECTEDPROPERTIESPANEL_H
#define CRYINCLUDE_EDITOR_REFLECTEDPROPERTIESPANEL_H
#pragma once
#include "Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.h"
#include "Util/Variable.h"
/////////////////////////////////////////////////////////////////////////////
// ReflectedPropertiesPanel dialog
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
//This class is a port of ReflectedPropertiesPanel to use the ReflectedPropertyControl
class SANDBOX_API ReflectedPropertiesPanel
: public ReflectedPropertyControl
{
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
public:
ReflectedPropertiesPanel(QWidget* pParent = nullptr); // standard constructor
void DeleteVars();
void AddVars(class CVarBlock* vb, const ReflectedPropertyControl::UpdateVarCallback& func = nullptr, const char* category = nullptr);
void SetVarBlock(class CVarBlock* vb, const ReflectedPropertyControl::UpdateVarCallback& func = nullptr, const char* category = nullptr);
protected:
void OnPropertyChanged(IVariable* pVar);
protected:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
TSmartPtr<CVarBlock> m_varBlock;
std::list<ReflectedPropertyControl::UpdateVarCallback> m_updateCallbacks;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
#endif // CRYINCLUDE_EDITOR_REFLECTEDPROPERTIESPANEL_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,380 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_EDITOR_UTILS_REFLECTEDPROPERTYCTRL_H
#define CRYINCLUDE_EDITOR_UTILS_REFLECTEDPROPERTYCTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include "Include/EditorCoreAPI.h"
#include "ReflectedPropertyItem.h"
#include "ReflectedVar.h"
#include <QFrame>
#endif
class QLineEdit;
class QLabel;
class QVBoxLayout;
class PropertyCard;
class QScrollArea;
namespace AzToolsFramework {
class ReflectedPropertyEditor;
class PropertyRowWidget;
class ComponentEditorHeader;
}
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
//ReflectedPropertyEditor-based implementation of the MFC CPropertyCtrl API
class EDITOR_CORE_API ReflectedPropertyControl
: public QWidget
, public AzToolsFramework::IPropertyEditorNotify
{
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
Q_OBJECT
public:
//! For alternative undo.
typedef Functor1<IVariable*> UndoCallback;
explicit ReflectedPropertyControl(QWidget* parent = nullptr, Qt::WindowFlags windowFlags = Qt::WindowFlags());
void Setup(bool showScrollbars = true, int labelWidth = 150);
ReflectedPropertyItem* AddVarBlock(CVarBlock* varBlock, const char* szCategory = nullptr);
void CreateItems(XmlNodeRef node);
void CreateItems(XmlNodeRef node, CVarBlockPtr& varBlock, IVariable::OnSetCallback func, bool splitCamelCaseIntoWords = false);
// Replace category item contents with the specified var block.
virtual void ReplaceVarBlock(IVariable* categoryItem, CVarBlock* varBlock);
//replace top-level var block. (used to port ctrl->ReplaceVarBlock(ctrl->GetRootItem(), varBlock);
virtual void ReplaceRootVarBlock(CVarBlock* newVarBlock);
void RemoveAllItems();
bool FindVariable(IVariable* categoryItem) const;
//! When item change, this callback fired variable that changed.
typedef Functor1<IVariable*> UpdateVarCallback;
//! When item change, update object.
typedef Functor1<IVariable*> UpdateObjectCallback;
//! When selection changes, this callback fired variable that changed.
typedef Functor1<IVariable*> SelChangeCallback;
/** Set update callback to be used for this property window.
*/
void SetUpdateCallback(const UpdateVarCallback& callback);
void ClearUpdateCallback() { m_updateVarFunc = nullptr; }
void SetUpdateObjectCallback(UpdateObjectCallback callback) { m_updateObjectFunc = callback; }
void ClearUpdateObjectCallback() { m_updateObjectFunc = nullptr; }
/** Set selchange callback to be used for this property window.
*/
void SetSelChangeCallback(SelChangeCallback callback);
//set a key that can be used to save/restore expanded state.
void SetSavedStateKey(AZ::u32 key);
void ExpandAll();
void CollapseAll();
virtual void Expand(ReflectedPropertyItem* item, bool expand);
void ExpandAllChildren(ReflectedPropertyItem* item, bool recursive);
//IPropertyEditorNotify Interface
void BeforePropertyModified([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void SetPropertyEditingActive([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingComplete([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SealUndoStack() override {}
void RequestPropertyContextMenu(AzToolsFramework::InstanceDataNode*, const QPoint&) override;
void PropertySelectionChanged(AzToolsFramework::InstanceDataNode *pNode, bool selected) override;
void SetStoreUndoByItems(bool bStoreUndoByItems) { m_bStoreUndoByItems = bStoreUndoByItems; }
bool IsStoreUndoByItems() const { return m_bStoreUndoByItems; }
void ClearSelection();
ReflectedPropertyItem* GetSelectedItem();
virtual void SelectItem(ReflectedPropertyItem* item);
QVector<ReflectedPropertyItem*> GetSelectedItems();
/** Set alternative undo callback.
*/
void SetUndoCallback(UndoCallback& callback);
void ClearUndoCallback();
/** Enable of disable calling update callback when some values change.
*/
void EnableUpdateCallback(bool bEnable);
void SetDeferredUpdateCallback(bool deferred);
// Control is grayed, but is not readonly.
void SetGrayed(bool grayed);
// Sets control to be read only, User cannot modify content of properties.
void SetReadOnly(bool readonly);
void SetMultiSelect(bool multiSelect);
void EnableNotifyWithoutValueChange(bool bFlag);
void CopyItem(XmlNodeRef rootNode, ReflectedPropertyItem* pItem, bool bRecursively);
// set to false if you don't want to receive callbacks when the item is not modified (when items are expanded etc)
void SetCallbackOnNonModified(bool bEnable) { m_bSendCallbackOnNonModified = bEnable; }
void ReloadValues();
//whether to group child properties alphabetically under expanding elements
void SetGroupProperties(bool group);
//whether to sort child properties alphabetically
void SetSortProperties(bool sort);
//whether to show line edit for filtering properties
void SetShowFilterWidget(bool showFilter);
// It doesn't add any property item, but instead it updates all property items with
// a new variable block (sets display value, flags and user data).
virtual void UpdateVarBlock(CVarBlock* pVarBlock);
//! Find item that reference specified property.
ReflectedPropertyItem* FindItemByVar(IVariable* pVar);
ReflectedPropertyItem* GetRootItem();
int GetContentHeight() const;
int GetVisibleHeight() const {return GetContentHeight();}
//whether this control is a section of a TwoColumnPropertyCtrl (so we can show correct copy/paste options)
void SetIsTwoColumnCtrlSection(bool isSection);
struct SCustomPopupItem
{
typedef Functor0 Callback;
QString m_text;
Callback m_callback;
SCustomPopupItem(const QString& text, const Functor0& callback)
: m_text(text)
, m_callback(callback) {}
};
struct SCustomPopupMenu
{
typedef Functor1<int> Callback;
QString m_text;
Callback m_callback;
QStringList m_subMenuText;
SCustomPopupMenu(const QString& text, const Callback& callback, const QStringList& subMenuText)
: m_text(text)
, m_callback(callback)
, m_subMenuText(subMenuText) {}
};
void AddCustomPopupMenuPopup(const QString& text, const Functor1<int>& handler, const QStringList& items);
void RemoveCustomPopupMenuPopup(const QString& text);
void AddCustomPopupMenuItem(const QString& text, const SCustomPopupItem::Callback handler);
void RemoveCustomPopupMenuItem(const QString& text);
AzToolsFramework::PropertyRowWidget* FindPropertyRowWidget(ReflectedPropertyItem* item);
QSize sizeHint() const override;
AzToolsFramework::ReflectedPropertyEditor* GetEditor() { return m_editor; }
void SetValuesFromNode(XmlNodeRef rootNode);
public slots:
//invalidates attributes and values
void InvalidateCtrl(bool queued = true);
void RebuildCtrl(bool queued = true);
void SetTitle(const QString &title);
void OnCopy(QVector<ReflectedPropertyItem*> items, bool bRecursively);
void OnCopyAll();
void OnCopyAll(XmlNodeRef node);
void OnPaste();
Q_SIGNALS:
void CopyAllSections();
void PasteAllSections();
protected:
friend class ReflectedPropertyItem;
virtual void OnItemChange(ReflectedPropertyItem* item, bool deferCallbacks = true);
CReflectedVar* GetReflectedVarFromCallbackInstance(AzToolsFramework::InstanceDataNode* pNode);
void RecreateAllItems();
// only shows items containing the string in their name. All items shown if string is empty.
void RestrictToItemsContaining(const QString& searchName);
bool CallUndoFunc(ReflectedPropertyItem* item);
virtual void UpdateVarBlock(ReflectedPropertyItem* pPropertyItem, IVariableContainer* pSourceContainer, IVariableContainer* pTargetContainer);
void ClearVarBlock();
private slots:
void DoUpdateCallback(IVariable *var);
void DoUpdateObjectCallback(IVariable *var);
private:
AzToolsFramework::ReflectedPropertyEditor* m_editor;
QLineEdit* m_filterLineEdit;
QWidget* m_filterWidget;
QLabel* m_titleLabel;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
_smart_ptr<CVarBlock> m_pVarBlock;
_smart_ptr<ReflectedPropertyItem> m_root;
AZStd::unique_ptr<CPropertyContainer> m_rootContainer;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZ::SerializeContext* m_serializeContext;
bool m_bEnableCallback;
QString m_filterString;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
UpdateVarCallback m_updateVarFunc;
UpdateObjectCallback m_updateObjectFunc;
SelChangeCallback m_selChangeFunc;
UndoCallback m_undoFunc;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
bool m_bStoreUndoByItems;
bool m_bForceModified;
bool m_groupProperties;
bool m_sortProperties;
bool m_bSendCallbackOnNonModified;
bool m_initialized;
bool m_isTwoColumnSection;
//custom popup menu
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
std::vector<SCustomPopupItem> m_customPopupMenuItems;
std::vector<SCustomPopupMenu> m_customPopupMenuPopups;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
template<typename T>
void RemoveCustomPopup(const QString& text, T& customPopup);
};
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
class EDITOR_CORE_API TwoColumnPropertyControl
: public QWidget
{
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
Q_OBJECT
public:
TwoColumnPropertyControl(QWidget* parent = nullptr);
void Setup(bool showScrollbars = true, int labelWidth = 150);
void AddVarBlock(CVarBlock* varBlock, const char* szCategory = nullptr);
// Replace category item contents with the specified var block.
virtual void ReplaceVarBlock(IVariable* categoryItem, CVarBlock* varBlock);
void RemoveAllItems();
bool FindVariable(IVariable* categoryItem) const;
void InvalidateCtrl();
void RebuildCtrl();
void SetStoreUndoByItems(bool bStoreUndoByItems);
/** Set alternative undo callback.
*/
void SetUndoCallback(ReflectedPropertyControl::UndoCallback callback);
void ClearUndoCallback();
/** Enable of disable calling update callback when some values change.
*/
void EnableUpdateCallback(bool bEnable);
void SetUpdateCallback(ReflectedPropertyControl::UpdateVarCallback callback);
// Control is grayed, but is not readonly.
void SetGrayed(bool grayed);
//set a key that can be used to save/restore expanded state.
void SetSavedStateKey(const QString& key);
void ExpandAllChildren(ReflectedPropertyItem* item, bool recursive);
void ExpandAllChildren(bool recursive);
void ReloadItems();
void OnCopyAll();
void OnPaste();
protected:
void resizeEvent(QResizeEvent *event) override;
private:
void ToggleTwoColumnLayout();
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QVector<PropertyCard*> m_controlList;
QVector<_smart_ptr<CVarBlock>> m_varBlockList;
_smart_ptr<CVarBlock> m_pVarBlock;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
QWidget *m_leftContainer;
QWidget *m_rightContainer;
QScrollArea *m_leftScrollArea;
QScrollArea *m_rightScrollArea;
bool m_twoColumns;
static const int minimumColumnWidth = 320;
static const int minimumTwoColumnWidth = 660;
};
class PropertyCard
: public QFrame
{
Q_OBJECT
public:
PropertyCard(QWidget* parent = nullptr);
void AddVarBlock(CVarBlock *varBlock);
ReflectedPropertyControl* GetControl();
void SetExpanded(bool expanded);
bool IsExpanded() const;
Q_SIGNALS:
void OnExpansionContractionDone();
private:
void OnExpanderChanged(bool expanded);
AzToolsFramework::ComponentEditorHeader* m_header = nullptr;
ReflectedPropertyControl* m_propertyEditor = nullptr;
};
#endif // CRYINCLUDE_EDITOR_UTILS_REFLECTEDPROPERTYCTRL_H
@@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/reflectedPropertyCtrl/img">
<file alias="apply.png">resources/apply.png</file>
<file alias="file_browse.png">resources/file_browse.png</file>
</qresource>
</RCC>
@@ -0,0 +1,692 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 "EditorDefs.h"
#include "ReflectedPropertyItem.h"
// AzToolsFramework
#include <AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx>
// Editor
#include "ReflectedVarWrapper.h"
#include "ReflectedPropertyCtrl.h"
#include "Undo/UndoVariableChange.h"
// default number of increments to cover the range of a property - determined experimentally by feel
const float ReflectedPropertyItem::s_DefaultNumStepIncrements = 500.0f;
//A ReflectedVarAdapter for holding IVariableContainers
//The extra ReflectedVarAdapter is the extra case of a container (has children) but also has a value itself.
//An example is an IVariable array whose type is forced to IVariable::DT_TEXTURE. The base Ivariable has a texture,
//but it also has children that are parameters of the texture. The ReflectedPropertyEditor does not support this case
//so we work around by adding the base property to the list of children and showing the value of the base property
//in the container value space instead of "X Elements"
static ColorF StringToColor(const QString &value)
{
ColorF color;
float r, g, b, a;
int res = azsscanf(value.toUtf8().data(), "%f,%f,%f,%f", &r, &g, &b, &a);
if (res == 4)
{
color.Set(r, g, b, a);
}
else if (res == 3)
{
color.Set(r, g, b);
}
else
{
unsigned abgr;
azsscanf(value.toUtf8().data(), "%u", &abgr);
color = ColorF(abgr);
}
return color;
}
class ReflectedVarContainerAdapter : public ReflectedVarAdapter
{
public:
ReflectedVarContainerAdapter(ReflectedPropertyItem *item, ReflectedPropertyControl *control, ReflectedVarAdapter *variableAdapter = nullptr)
: m_extraVariableAdapter(variableAdapter)
, m_item(item)
, m_propertyCtrl(control)
, m_containerVar(new CPropertyContainer(AZStd::string()))
{
m_containerVar->SetAutoExpand(false);
}
void SetVariable(IVariable *pVariable) override
{
if (m_extraVariableAdapter)
m_extraVariableAdapter->SetVariable(pVariable);
//Check whether the parent container has autoExpand flag set, and if so, the autoexpand flag for this item
//We need to do this because the default IVariable flags has the item expanded, so most items are expanded,
//but the ReflectedPropertyEditor expands all ancestors if any item is expanded.
//This is not what we want -- the old property editor did not expand ancestors. In case of Material editor,
//this expansion can be really expensive!
const bool parentIsAutoExpand = m_item->GetParent() == nullptr || m_item->GetParent()->GetContainer() == nullptr || m_item->GetParent()->GetContainer()->m_containerVar->AutoExpand();
const bool bDefaultExpand = (pVariable->GetFlags() & IVariable::UI_COLLAPSED) == 0 || (pVariable->GetFlags() & IVariable::UI_AUTO_EXPAND);
m_containerVar->SetAutoExpand(parentIsAutoExpand && bDefaultExpand);
UpdateCommon(pVariable, pVariable);
}
//helps implement ReflectedPropertyControl::ReplaceVarBlock
void ReplaceVarBlock(CVarBlock *varBlock)
{
m_containerVar->Clear();
UpdateCommon(m_item->GetVariable(), varBlock);
}
void SyncReflectedVarToIVar(IVariable *pVariable) override
{
if (m_extraVariableAdapter)
{
m_extraVariableAdapter->SyncReflectedVarToIVar(pVariable);
//update text on parent container. Do not have control update attributes since this will happen anyway as part of updating ReflectedVar
updateContainerText(pVariable, false);
}
};
void SyncIVarToReflectedVar(IVariable *pVariable) override
{
if (m_extraVariableAdapter)
{
m_extraVariableAdapter->SyncIVarToReflectedVar(pVariable);
//update text on parent container. Force control to update attributes since this doesn't normally happen when updating an IVar from ReflectedVar
updateContainerText(pVariable, true);
}
};
CReflectedVar *GetReflectedVar() override { return m_containerVar.data(); }
bool Contains(CReflectedVar *var) override { return var == m_containerVar.data() || (m_extraVariableAdapter && m_extraVariableAdapter->GetReflectedVar() == var); }
private:
void UpdateCommon(IVariable *nameVariable, IVariableContainer *childContainer)
{
m_containerVar->m_varName = nameVariable->GetHumanName().toUtf8().data();
m_containerVar->m_description = nameVariable->GetDescription().toUtf8().data();
if (m_extraVariableAdapter)
{
m_containerVar->AddProperty(m_extraVariableAdapter->GetReflectedVar());
}
//Handle adding empty varblock
if (!childContainer)
return;
for (int i = 0; i < childContainer->GetNumVariables(); i++)
{
AddChild(childContainer->GetVariable(i));
}
}
void AddChild(IVariable *var)
{
if (var->GetFlags() & IVariable::UI_INVISIBLE)
return;
ReflectedPropertyItemPtr item = new ReflectedPropertyItem(m_propertyCtrl, m_item);
item->SetVariable(var);
m_containerVar->AddProperty(item->GetReflectedVar());
}
void updateContainerText(IVariable *pVariable, bool updateAttributes)
{
//set text of the container to the value of the main variable. If it's empty, use space, otherwise ReflectedPropertyEditor doesn't update it!
m_containerVar->SetValueText(pVariable->GetDisplayValue().isEmpty() ? AZStd::string(" ") : AZStd::string(pVariable->GetDisplayValue().toUtf8().data()));
if (updateAttributes)
m_propertyCtrl->InvalidateCtrl();
}
private:
//optional adapter for case where this item contains a variable in addition to a container of variables.
ReflectedVarAdapter *m_extraVariableAdapter;
QScopedPointer<CPropertyContainer> m_containerVar;
ReflectedPropertyItem *m_item;
ReflectedPropertyControl *m_propertyCtrl;
};
ReflectedPropertyItem::ReflectedPropertyItem(ReflectedPropertyControl *control, ReflectedPropertyItem *parent)
: m_pVariable(nullptr)
, m_reflectedVarAdapter(nullptr)
, m_reflectedVarContainerAdapter(nullptr)
, m_parent(parent)
, m_propertyCtrl(control)
, m_syncingIVar(false)
, m_strNoScriptDefault("<<undefined>>")
, m_strScriptDefault(m_strNoScriptDefault)
{
m_type = ePropertyInvalid;
m_modified = false;
if (parent)
parent->AddChild(this);
}
ReflectedPropertyItem::~ReflectedPropertyItem()
{
// just to make sure we dont double (or infinitely recurse...) delete
AddRef();
if (m_pVariable)
ReleaseVariable();
RemoveAllChildren();
}
void ReflectedPropertyItem::SetVariable(IVariable *var)
{
if (var == m_pVariable)
{
// Early exit optimization if setting the same var as the current var.
// A common use case, in Track View for example, is to re-use the save var for a property when switching to a new
// instance of the same variable. The visible display of the value is often handled by invalidating the property,
// but the non-visible attributes, i.e. the range values, are usually set using this method. Thus we reset the ranges
// explicitly here when the Ivariable var is the same
if (m_reflectedVarAdapter)
m_reflectedVarAdapter->UpdateRangeLimits(var);
return;
}
_smart_ptr<IVariable> pInputVar = var;
// Release previous variable.
if (m_pVariable)
ReleaseVariable();
m_pVariable = pInputVar;
assert(m_pVariable != NULL);
m_pVariable->AddOnSetCallback(functor(*this, &ReflectedPropertyItem::OnVariableChange));
m_pVariable->AddOnSetEnumCallback(functor(*this, &ReflectedPropertyItem::OnVariableEnumChange));
// Fetch base parameter description
Prop::Description desc(m_pVariable);
m_type = desc.m_type;
switch (m_type)
{
case ePropertyVector2:
m_reflectedVarAdapter = new ReflectedVarVector2Adapter;
break;
case ePropertyVector:
m_reflectedVarAdapter = new ReflectedVarVector3Adapter;
break;
case ePropertyVector4:
m_reflectedVarAdapter = new ReflectedVarVector4Adapter;
break;
case ePropertyFloat:
case ePropertyAngle:
//if the Description has a valid global enumDB lookup, edit as an enum, otherwise use normal float editor
if (desc.m_pEnumDBItem)
m_reflectedVarAdapter = new ReflectedVarDBEnumAdapter;
else
m_reflectedVarAdapter = new ReflectedVarFloatAdapter;
break;
case ePropertyInt:
//if the Description has a valid global enumDB lookup, edit as an enum, otherwise use normal int editor
if (desc.m_pEnumDBItem)
m_reflectedVarAdapter = new ReflectedVarDBEnumAdapter;
else
m_reflectedVarAdapter = new ReflectedVarIntAdapter;
break;
case ePropertyBool:
m_reflectedVarAdapter = new ReflectedVarBoolAdapter;
break;
case ePropertyString:
//if the Description has a valid global enumDB lookup, edit as an enum, otherwise use normal string editor
if (desc.m_pEnumDBItem)
m_reflectedVarAdapter = new ReflectedVarDBEnumAdapter;
else
m_reflectedVarAdapter = new ReflectedVarStringAdapter;
break;
case ePropertySelection:
m_reflectedVarAdapter = new ReflectedVarEnumAdapter;
break;
case ePropertyAnimation:
m_reflectedVarAdapter = new ReflectedVarAnimationAdapter;
break;
case ePropertyColor:
m_reflectedVarAdapter = new ReflectedVarColorAdapter;
break;
case ePropertyUser:
m_reflectedVarAdapter = new ReflectedVarUserAdapter;
break;
case ePropertyShader:
case ePropertyMaterial:
case ePropertyEquip:
case ePropertyReverbPreset:
case ePropertyGameToken:
case ePropertyMissionObj:
case ePropertySequence:
case ePropertySequenceId:
case ePropertyLocalString:
case ePropertyLightAnimation:
case ePropertyParticleName:
case ePropertyFlare:
m_reflectedVarAdapter = new ReflectedVarGenericPropertyAdapter(desc.m_type);
break;
case ePropertyTexture:
case ePropertyModel:
case ePropertyGeomCache:
case ePropertyAudioTrigger:
case ePropertyAudioSwitch:
case ePropertyAudioSwitchState:
case ePropertyAudioRTPC:
case ePropertyAudioEnvironment:
case ePropertyAudioPreloadRequest:
case ePropertyFile:
m_reflectedVarAdapter = new ReflectedVarResourceAdapter;
break;
case ePropertyFloatCurve:
case ePropertyColorCurve:
m_reflectedVarAdapter = new ReflectedVarSplineAdapter(this, m_type);
break;
case ePropertyMotion:
m_reflectedVarAdapter = new ReflectedVarMotionAdapter;
break;
default:
break;
}
const bool hasChildren = (m_pVariable->GetNumVariables() > 0 || desc.m_type == ePropertyTable || m_pVariable->GetType() == IVariable::ARRAY);
//const bool isNotContainerType = (m_pVariable->GetType() != IVariable::ARRAY && desc.m_type != ePropertyTable && desc.m_type != ePropertyInvalid);
if (hasChildren )
{
m_reflectedVarContainerAdapter = new ReflectedVarContainerAdapter(this, m_propertyCtrl, m_reflectedVarAdapter);
m_reflectedVarAdapter = m_reflectedVarContainerAdapter;
}
if (m_reflectedVarAdapter)
{
m_reflectedVarAdapter->SetVariable(m_pVariable);
m_reflectedVarAdapter->SyncReflectedVarToIVar(m_pVariable);
}
m_modified = false;
}
void ReflectedPropertyItem::ReplaceVarBlock(CVarBlock *varBlock)
{
RemoveAllChildren();
if (m_reflectedVarAdapter)
m_reflectedVarAdapter->ReplaceVarBlock(varBlock);
}
void ReflectedPropertyItem::AddChild(ReflectedPropertyItem *item)
{
assert(item);
m_childs.push_back(item);
}
void ReflectedPropertyItem::RemoveAllChildren()
{
for (int i = 0; i < m_childs.size(); i++)
{
m_childs[i]->m_parent = 0;
}
m_childs.clear();
}
void ReflectedPropertyItem::RemoveChild(ReflectedPropertyItem* item)
{
for (int i = 0; i < m_childs.size(); i++)
{
if (m_childs[i] == item)
{
item->m_parent = nullptr;
m_childs.erase(m_childs.begin() + i);
return;
}
}
}
CReflectedVar * ReflectedPropertyItem::GetReflectedVar() const
{
return m_reflectedVarAdapter ? m_reflectedVarAdapter->GetReflectedVar() : nullptr;
}
ReflectedPropertyItem * ReflectedPropertyItem::findItem(CReflectedVar *var)
{
if (m_reflectedVarAdapter && m_reflectedVarAdapter->Contains(var) )
return this;
for (auto child : m_childs)
{
ReflectedPropertyItem *result = child->findItem(var);
if (result)
return result;
}
return nullptr;
}
ReflectedPropertyItem * ReflectedPropertyItem::findItem(IVariable *var)
{
if (m_pVariable == var)
return this;
for (auto child : m_childs)
{
ReflectedPropertyItem *result = child->findItem(var);
if (result)
return result;
}
return nullptr;
}
ReflectedPropertyItem* ReflectedPropertyItem::findItem(const QString &name)
{
if (m_pVariable && m_pVariable->GetHumanName() == name)
return this;
for (auto child : m_childs)
{
ReflectedPropertyItem *result = child->findItem(name);
if (result)
return result;
}
return nullptr;
}
ReflectedPropertyItem * ReflectedPropertyItem::FindItemByFullName(const QString& fullName)
{
if (GetFullName() == fullName)
{
return this;
}
for (int i = 0; i < m_childs.size(); ++i)
{
auto pFound = m_childs[i]->FindItemByFullName(fullName);
if (pFound)
{
return pFound;
}
}
return nullptr;
}
QString ReflectedPropertyItem::GetName() const
{
return m_pVariable ? m_pVariable->GetHumanName() : QString();
}
QString ReflectedPropertyItem::GetFullName() const
{
if (m_parent && m_pVariable)
{
return m_parent->GetFullName() + "::" + m_pVariable->GetName();
}
else if (m_pVariable)
{
return m_pVariable->GetName();
}
else
{
return {};
}
}
void ReflectedPropertyItem::OnReflectedVarChanged()
{
m_syncingIVar = true;
if (m_reflectedVarAdapter)
{
std::unique_ptr<CUndo> undo;
if (!CUndo::IsRecording())
{
if (!m_propertyCtrl->CallUndoFunc(this))
undo.reset(new CUndo((m_pVariable->GetHumanName() + " Modified").toUtf8().data()));
}
m_reflectedVarAdapter->SyncIVarToReflectedVar(m_pVariable);
if (m_propertyCtrl->IsStoreUndoByItems() && CUndo::IsRecording())
CUndo::Record(new CUndoVariableChange(m_pVariable, "PropertyChange"));
m_modified = true;
}
m_syncingIVar = false;
}
void ReflectedPropertyItem::SyncReflectedVarToIVar()
{
if (m_reflectedVarAdapter)
{
m_reflectedVarAdapter->SyncReflectedVarToIVar(m_pVariable);
}
}
void ReflectedPropertyItem::ReleaseVariable()
{
if (m_pVariable)
{
// Unwire all from variable.
m_pVariable->RemoveOnSetCallback(functor(*this, &ReflectedPropertyItem::OnVariableChange));
m_pVariable->RemoveOnSetEnumCallback(functor(*this, &ReflectedPropertyItem::OnVariableEnumChange));
}
m_pVariable = 0;
delete m_reflectedVarAdapter;
m_reflectedVarAdapter = nullptr;
}
void ReflectedPropertyItem::OnVariableChange(IVariable* pVar)
{
assert(pVar != 0 && pVar == m_pVariable);
if (m_syncingIVar)
return;
// When variable changes, invalidate UI.
m_modified = true;
if (m_reflectedVarAdapter)
{
m_reflectedVarAdapter->OnVariableChange(pVar);
}
SyncReflectedVarToIVar();
m_propertyCtrl->InvalidateCtrl();
}
void ReflectedPropertyItem::OnVariableEnumChange([[maybe_unused]] IVariable* pVar)
{
if (m_reflectedVarAdapter && m_reflectedVarAdapter->UpdateReflectedVarEnums())
{
m_propertyCtrl->InvalidateCtrl(true);
}
}
void ReflectedPropertyItem::ReloadValues()
{
m_modified = false;
if (m_pVariable)
SetVariable(m_pVariable);
for (int i = 0; i < GetChildCount(); i++)
{
GetChild(i)->ReloadValues();
}
SyncReflectedVarToIVar();
}
/** Changes value of item.
*/
void ReflectedPropertyItem::SetValue(const QString& sValue, bool bRecordUndo, bool bForceModified)
{
if (!m_pVariable)
{
return;
}
_smart_ptr<ReflectedPropertyItem> holder = this; // Make sure we are not released during this function.
QString value = sValue;
switch (m_type)
{
case ePropertyBool:
if (QString::compare(value, "true", Qt::CaseInsensitive) == 0 || value.toInt() != 0)
{
value = "1";
}
else
{
value = "0";
}
break;
case ePropertyVector2:
if (!value.contains(','))
{
value = value + ", " + value;
}
break;
case ePropertyVector4:
if (!value.contains(','))
{
value = value + ", " + value + ", " + value + ", " + value;
}
break;
case ePropertyVector:
if (!value.contains(','))
{
value = value + ", " + value + ", " + value;
}
break;
case ePropertyTexture:
case ePropertyModel:
case ePropertyMaterial:
value.replace('\\', '/');
break;
}
// correct the length of value
switch (m_type)
{
case ePropertyTexture:
case ePropertyModel:
case ePropertyMaterial:
case ePropertyFile:
if (value.length() >= MAX_PATH)
{
value = value.left(MAX_PATH);
}
break;
}
bool bModified = bForceModified || m_pVariable->GetDisplayValue() != value;
bool bStoreUndo = (m_pVariable->GetDisplayValue() != value || bForceModified) && bRecordUndo;
std::unique_ptr<CUndo> undo;
if (bStoreUndo && !CUndo::IsRecording())
{
if (!m_propertyCtrl->CallUndoFunc(this))
{
undo.reset(new CUndo((GetName() + " Modified").toUtf8().data()));
}
}
if (m_pVariable)
{
if (bModified)
{
if (m_propertyCtrl->IsStoreUndoByItems() && bStoreUndo && CUndo::IsRecording())
{
CUndo::Record(new CUndoVariableChange(m_pVariable, "PropertyChange"));
}
if (bForceModified)
{
m_pVariable->SetForceModified(true);
}
switch (m_type)
{
case ePropertyColor:
{
ColorF color = StringToColor(value);
if (m_pVariable->GetType() == IVariable::VECTOR)
{
m_pVariable->Set(color.toVec3());
}
else
{
m_pVariable->Set(static_cast<int>(color.pack_abgr8888()));
}
break;
}
case ePropertyInvalid:
break;
default:
m_pVariable->SetDisplayValue(value);
break;
}
}
}
else
{
if (bModified)
{
m_modified = true;
// If Value changed mark document modified.
// Notify parent that this Item have been modified.
m_propertyCtrl->OnItemChange(this);
}
}
}
void ReflectedPropertyItem::SendOnItemChange()
{
m_propertyCtrl->OnItemChange(this);
}
void ReflectedPropertyItem::ExpandAllChildren(bool recursive)
{
Expand(true);
for (auto child : m_childs)
{
if (recursive)
{
child->ExpandAllChildren(recursive);
}
else
{
child->Expand(true);
}
}
}
void ReflectedPropertyItem::Expand(bool expand)
{
AzToolsFramework::PropertyRowWidget *widget = m_propertyCtrl->FindPropertyRowWidget(this);
if (widget)
{
widget->SetExpanded(expand);
}
}
QString ReflectedPropertyItem::GetPropertyName() const
{
return m_pVariable ? m_pVariable->GetHumanName() : QString();
}
@@ -0,0 +1,170 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_EDITOR_UTILS_REFLECTEDPROPERTYITEM_H
#define CRYINCLUDE_EDITOR_UTILS_REFLECTEDPROPERTYITEM_H
#pragma once
#include "Util/Variable.h"
#include <Util/VariablePropertyType.h>
namespace AzToolsFramework {
class ReflectedPropertyEditor;
}
class CReflectedVar;
class CPropertyContainer;
class ReflectedPropertyControl;
class ReflectedVarAdapter;
class ReflectedVarContainerAdapter;
// Class representing a property inside a ReflectedPropertyCtrl.
// It contains the IVariable and corresponding CReflectedVar for that property
// and any children properties if the IVariable is a container.
// This class is loosely based on the MFC-based CPropertyItem to make porting easier.
// The CPropertyItem created editor widgets for each property type, but this class
// just holds a CReflectedVar and updates it's values. The editing is done by the
// reflection system and registered property handlers for each CReflectedVar.
class EDITOR_CORE_API ReflectedPropertyItem
: public CRefCountBase
{
public:
ReflectedPropertyItem(ReflectedPropertyControl* control, ReflectedPropertyItem* parent);
~ReflectedPropertyItem();
void SetVariable(IVariable* var);
IVariable* GetVariable() const { return m_pVariable; }
void ReplaceVarBlock(CVarBlock* varBlock);
CReflectedVar* GetReflectedVar() const;
ReflectedPropertyItem* findItem(CReflectedVar* var);
ReflectedPropertyItem* findItem(IVariable* var);
ReflectedPropertyItem* findItem(const QString &name);
ReflectedPropertyItem* FindItemByFullName(const QString& fullName);
//update the internal IVariable as result of ReflectedVar changing
void OnReflectedVarChanged();
//update the ReflectedVar to current value of IVar
void SyncReflectedVarToIVar();
//! Return true if this property item is modified.
bool IsModified() const { return m_modified; }
void ReloadValues();
ReflectedVarContainerAdapter* GetContainer() { return m_reflectedVarContainerAdapter; }
ReflectedPropertyItem* GetParent() { return m_parent; }
/** Get script default value of property item.
*/
virtual bool HasScriptDefault() const { return m_strScriptDefault != m_strNoScriptDefault; };
/** Get script default value of property item.
*/
virtual QString GetScriptDefault() const { return m_strScriptDefault; };
/** Set script default value of property item.
*/
virtual void SetScriptDefault(const QString& sScriptDefault) { m_strScriptDefault = sScriptDefault; };
/** Set script default value of property item.
*/
virtual void ClearScriptDefault() { m_strScriptDefault = m_strNoScriptDefault; };
/** Changes value of item.
*/
virtual void SetValue(const QString& sValue, bool bRecordUndo = true, bool bForceModified = false);
//hack for calling ReflectedPropertyControl::OnItemChange from a wrapper class
//this is used because changes to Splines should not actually change anything in the IVariable,
//but we need OnItemChanged as if the IVariable did change.
void SendOnItemChange();
void ExpandAllChildren(bool recursive);
void Expand(bool expand);
QString GetPropertyName() const;
void AddChild(ReflectedPropertyItem* item);
void RemoveAllChildren();
void RemoveChild(ReflectedPropertyItem* item);
// default number of increments to cover the range of a property
static const float s_DefaultNumStepIncrements;
// for a consistent Feel, compute the step size for a numerical slider for the specified min/max, rounded to precision
inline static float ComputeSliderStep(float sliderMin, float sliderMax, const float precision = .01f)
{
float step;
step = int_round(((sliderMax - sliderMin) / ReflectedPropertyItem::s_DefaultNumStepIncrements) / precision) * precision;
// prevent rounding down to zero
return (step > precision) ? step : precision;
}
protected:
friend class ReflectedPropertyControl;
//! Release used variable.
void ReleaseVariable();
//! Callback called when variable change.
void OnVariableChange(IVariable* var);
void OnVariableEnumChange(IVariable* var);
public:
//! Get number of child nodes.
int GetChildCount() const { return m_childs.size(); };
//! Get Child by id.
ReflectedPropertyItem* GetChild(int index) const { return m_childs[index]; }
PropertyType GetType() const { return m_type; }
/** Get name of property item.
*/
virtual QString GetName() const;
QString GetFullName() const;
protected:
PropertyType m_type;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
//The variable being edited.
_smart_ptr<IVariable> m_pVariable;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
//holds the CReflectedVar and syncs its value with IVariable when either changes
ReflectedVarAdapter* m_reflectedVarAdapter;
ReflectedVarContainerAdapter* m_reflectedVarContainerAdapter;
ReflectedPropertyItem* m_parent;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
std::vector<_smart_ptr<ReflectedPropertyItem> > m_childs;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
ReflectedPropertyControl* m_propertyCtrl;
unsigned int m_modified : 1;
bool m_syncingIVar;
QString m_strNoScriptDefault;
QString m_strScriptDefault;
};
typedef _smart_ptr<ReflectedPropertyItem> ReflectedPropertyItemPtr;
#endif // CRYINCLUDE_EDITOR_UTILS_REFLECTEDPROPERTYITEM_H
@@ -0,0 +1,329 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 "EditorDefs.h"
#include "ReflectedVar.h"
// AzCore
#include <AzCore/Serialization/EditContext.h>
bool ReflectedVarInit::s_reflectionDone = false;
void ReflectedVarInit::setupReflection(AZ::SerializeContext* serializeContext)
{
if (!serializeContext)
return;
if (s_reflectionDone)
return;
s_reflectionDone = true;
serializeContext->Class< CReflectedVar>()
->Version(1)
->Field("description", &CReflectedVar::m_description)
->Field("varName", &CReflectedVar::m_varName);
serializeContext->Class <CReflectedVarAnimation, CReflectedVar >()
->Version(1)
->Field("animation", &CReflectedVarAnimation::m_animation)
->Field("entityID", &CReflectedVarAnimation::m_entityID)
;
serializeContext->Class <CReflectedVarResource, CReflectedVar >()
->Version(1)
->Field("path", &CReflectedVarResource::m_path)
->Field("propertyType", &CReflectedVarResource::m_propertyType)
;
serializeContext->Class< CReflectedVarColor, CReflectedVar>()
->Version(1)
->Field("color", &CReflectedVarColor::m_color);
serializeContext->Class< CReflectedVarUser, CReflectedVar>()
->Version(1)
->Field("value", &CReflectedVarUser::m_value)
->Field("enableEdit", &CReflectedVarUser::m_enableEdit)
->Field("title", &CReflectedVarUser::m_dialogTitle)
->Field("useTree", &CReflectedVarUser::m_useTree)
->Field("treeSeparator", &CReflectedVarUser::m_treeSeparator)
->Field("itemNames", &CReflectedVarUser::m_itemNames)
->Field("itemDescriptions", &CReflectedVarUser::m_itemDescriptions);
serializeContext->Class <CReflectedVarSpline, CReflectedVar >()
->Version(1)
->Field("spline", &CReflectedVarSpline::m_spline)
->Field("propertyType", &CReflectedVarSpline::m_propertyType)
;
serializeContext->Class< CPropertyContainer, CReflectedVar>()
->Version(1)
->Field("properties", &CPropertyContainer::m_properties);
serializeContext->Class <CReflectedVarMotion, CReflectedVar >()
->Version(1)
->Field("motion", &CReflectedVarMotion::m_motion)
->Field("assetId", &CReflectedVarMotion::m_assetId)
;
AZ::EditContext* ec = serializeContext->GetEditContext();
if (ec)
{
ec->Class< CReflectedVarAnimation >("VarAnimation", "Animation")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &CReflectedVarAnimation::varName)
->Attribute(AZ::Edit::Attributes::DescriptionTextOverride, &CReflectedVarAnimation::description)
;
ec->Class< CReflectedVarResource >("VarResource", "Resource")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &CReflectedVarResource::varName)
->Attribute(AZ::Edit::Attributes::DescriptionTextOverride, &CReflectedVarResource::description)
;
ec->Class< CReflectedVarUser >("VarUser", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &CReflectedVarUser::varName)
->Attribute(AZ::Edit::Attributes::Handler, AZ_CRC("ePropertyUser", 0x65b972c0))
;
ec->Class< CReflectedVarColor >("VarColor", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20))
->DataElement(AZ::Edit::UIHandlers::Color, &CReflectedVarColor::m_color, "Color", "")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &CReflectedVarColor::varName)
->Attribute(AZ::Edit::Attributes::DescriptionTextOverride, &CReflectedVarColor::description)
;
ec->Class< CReflectedVarSpline >("VarSpline", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &CReflectedVarSpline::varName)
->Attribute(AZ::Edit::Attributes::Handler, &CReflectedVarSpline::handler)
;
ec->Class< CPropertyContainer >("PropertyContainer", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20))
->DataElement(AZ::Edit::UIHandlers::Default, &CPropertyContainer::m_properties, "Properties", "")
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &CPropertyContainer::varName)
->Attribute(AZ::Edit::Attributes::DescriptionTextOverride, &CPropertyContainer::description)
->Attribute(AZ::Edit::Attributes::Visibility, &CPropertyContainer::GetVisibility)
->Attribute(AZ::Edit::Attributes::AutoExpand, &CPropertyContainer::m_autoExpand)
->Attribute(AZ::Edit::Attributes::ValueText, &CPropertyContainer::m_valueText) //will be ignored if blank
;
ec->Class< CReflectedVarMotion >("VarMotion", "Motion")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &CReflectedVarMotion::varName)
->Attribute(AZ::Edit::Attributes::DescriptionTextOverride, &CReflectedVarMotion::description)
;
}
CReflectedVarString::reflect(serializeContext);
CReflectedVarBool::reflect(serializeContext);
CReflectedVarFloat::reflect(serializeContext);
CReflectedVarInt::reflect(serializeContext);
CReflectedVarVector2::reflect(serializeContext);
CReflectedVarVector3::reflect(serializeContext);
CReflectedVarVector4::reflect(serializeContext);
CReflectedVarAny<AZStd::vector<AZStd::string>>::reflect(serializeContext);
CReflectedVarEnum<int>::reflect(serializeContext);
CReflectedVarEnum<AZStd::string>::reflect(serializeContext);
CReflectedVarGenericProperty::reflect(serializeContext);
}
template<class T>
void CReflectedVarAny<T>::reflect(AZ::SerializeContext* serializeContext)
{
static bool reflected = false;
if (reflected)
return;
reflected = true;
serializeContext->Class< CReflectedVarAny<T>, CReflectedVar>()
->Version(1)
->Field("value", &CReflectedVarAny<T>::m_value);
AZ::EditContext* ec = serializeContext->GetEditContext();
if (ec)
{
ec->Class< CReflectedVarAny<T> >("VarAny", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20))
->DataElement(AZ::Edit::UIHandlers::Default, &CReflectedVarAny<T>::m_value, "Value", "")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &CReflectedVarAny<T>::varName)
->Attribute(AZ::Edit::Attributes::DescriptionTextOverride, &CReflectedVarAny<T>::description)
;
}
}
template<class T, class R>
void CReflectedVarRanged<T, R>::reflect(AZ::SerializeContext* serializeContext)
{
static bool reflected = false;
if (reflected)
return;
reflected = true;
serializeContext->Class< CReflectedVarRanged<T, R>, CReflectedVar>()
->Version(1)
->Field("value", &CReflectedVarRanged<T, R>::m_value)
->Field("min", &CReflectedVarRanged<T, R>::m_minVal)
->Field("max", &CReflectedVarRanged<T, R>::m_maxVal)
->Field("step", &CReflectedVarRanged<T, R>::m_stepSize)
->Field("softMin", &CReflectedVarRanged<T, R>::m_softMinVal)
->Field("softMax", &CReflectedVarRanged<T, R>::m_softMaxVal)
;
AZ::EditContext* ec = serializeContext->GetEditContext();
if (ec)
{
ec->Class< CReflectedVarRanged<T, R> >("VarAny", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20))
->DataElement(AZ::Edit::UIHandlers::Slider, &CReflectedVarRanged<T, R>::m_value, "Value", "")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &CReflectedVarRanged<T, R>::varName)
->Attribute(AZ::Edit::Attributes::DescriptionTextOverride, &CReflectedVarRanged<T, R>::description)
->Attribute(AZ::Edit::Attributes::Min, &CReflectedVarRanged<T, R>::minValue)
->Attribute(AZ::Edit::Attributes::Max, &CReflectedVarRanged<T, R>::maxValue)
->Attribute(AZ::Edit::Attributes::Step, &CReflectedVarRanged<T, R>::stepSize)
->Attribute(AZ::Edit::Attributes::SoftMin, &CReflectedVarRanged<T, R>::softMinVal)
->Attribute(AZ::Edit::Attributes::SoftMax, &CReflectedVarRanged<T, R>::softMaxVal)
;
}
}
template<class T>
void CReflectedVarEnum<T>::reflect(AZ::SerializeContext* serializeContext)
{
static bool reflected = false;
if (reflected)
return;
reflected = true;
serializeContext->Class< CReflectedVarEnum<T>, CReflectedVar>()
->Version(1)
->Field("value", &CReflectedVarEnum<T>::m_value)
->Field("selectedName", &CReflectedVarEnum<T>::m_selectedEnumName)
->Field("availableValues", &CReflectedVarEnum<T>::m_enums)
;
AZ::EditContext* ec = serializeContext->GetEditContext();
if (ec)
{
ec->Class< CReflectedVarEnum<T> >("Enum Variable", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20))
->DataElement(AZ::Edit::UIHandlers::ComboBox, &CReflectedVarEnum<T>::m_selectedEnumName, "Value", "")
->Attribute(AZ::Edit::Attributes::StringList, &CReflectedVarEnum<T>::GetEnums)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &CReflectedVarEnum<T>::OnEnumChanged)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &CReflectedVarEnum<T>::varName)
->Attribute(AZ::Edit::Attributes::DescriptionTextOverride, &CReflectedVarEnum<T>::description)
;
}
}
void CReflectedVarGenericProperty::reflect(AZ::SerializeContext* serializeContext)
{
static bool reflected = false;
if (reflected)
return;
reflected = true;
serializeContext->Class< CReflectedVarGenericProperty, CReflectedVar>()
->Version(1)
->Field("value", &CReflectedVarGenericProperty::m_value)
->Field("propertyType", &CReflectedVarGenericProperty::m_propertyType)
;
AZ::EditContext* ec = serializeContext->GetEditContext();
if (ec)
{
ec->Class< CReflectedVarGenericProperty >("GenericProperty", "GenericProperty")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &CReflectedVarGenericProperty::varName)
->Attribute(AZ::Edit::Attributes::DescriptionTextOverride, &CReflectedVarGenericProperty::description)
->Attribute(AZ::Edit::Attributes::Handler, &CReflectedVarGenericProperty::handler)
;
}
}
AZ::u32 CReflectedVarSpline::handler()
{
switch (m_propertyType)
{
case ePropertyFloatCurve:
return AZ_CRC("ePropertyFloatCurve", 0x7440ccce);
case ePropertyColorCurve:
return AZ_CRC("ePropertyColorCurve", 0xa30da4ec);
default:
AZ_Assert(false, "CReflectedVarSpline property type must be ePropertyFloatCurve or ePropertyColorCurve");
return AZ::Edit::UIHandlers::Default;
}
}
AZ::u32 CReflectedVarGenericProperty::handler()
{
switch (m_propertyType)
{
case ePropertyShader:
return AZ_CRC("ePropertyShader", 0xc40932f1);
case ePropertyMaterial:
return AZ_CRC("ePropertyMaterial", 0xf324dffa);
case ePropertyEquip:
return AZ_CRC("ePropertyEquip", 0x66ffd290);
case ePropertyReverbPreset:
return AZ_CRC("ePropertyReverbPreset", 0x51469f38);
case ePropertyDeprecated0:
return AZ_CRC("ePropertyCustomAction", 0x4ffa5ba5);
case ePropertyGameToken:
return AZ_CRC("ePropertyGameToken", 0x34855b6f);
case ePropertyMissionObj:
return AZ_CRC("ePropertyMissionObj", 0x4a2d0dc8);
case ePropertySequence:
return AZ_CRC("ePropertySequence", 0xdd1c7d44);
case ePropertySequenceId:
return AZ_CRC("ePropertySequenceId", 0x05983dcc);
case ePropertyLocalString:
return AZ_CRC("ePropertyLocalString", 0x0cd9609a);
case ePropertyLightAnimation:
return AZ_CRC("ePropertyLightAnimation", 0x277097da);
case ePropertyParticleName:
return AZ_CRC("ePropertyParticleName", 0xf44c7133);
case ePropertyFlare:
return AZ_CRC("ePropertyFlare", 0x5ce803df);
default:
AZ_Assert(false, "No property handlers defined for the property type");
return AZ_CRC("Default", 0xe35e00df);
}
}
void CPropertyContainer::AddProperty(CReflectedVar *property)
{
if (property)
m_properties.push_back(property);
}
void CPropertyContainer::Clear()
{
m_properties.clear();
}
@@ -0,0 +1,434 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_EDITOR_UTILS_REFLECTEDVAR_H
#define CRYINCLUDE_EDITOR_UTILS_REFLECTEDVAR_H
#pragma once
#include <AzCore/Serialization/SerializeContext.h>
#include <algorithm>
#include <limits>
#include "Util/VariablePropertyType.h"
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
//Base class for generic reflected variables
class CReflectedVar
{
public:
AZ_RTTI(CReflectedVar, "{9CF461B5-4093-4F7E-9A28-75531F0D046C}")
CReflectedVar() = default;
CReflectedVar(const AZStd::string& name)
: m_varName(name){}
virtual ~CReflectedVar(){}
AZStd::string m_varName;
AZStd::string m_description;
};
// Reflected container of reflected values. Also holds ePropertyTable data
class CPropertyContainer
: public CReflectedVar
{
public:
AZ_RTTI(CPropertyContainer, "{99500790-241A-4274-BAD8-C4510E869FC6}", CReflectedVar)
CPropertyContainer(const AZStd::string& name)
: CReflectedVar(name) {}
CPropertyContainer() = default;
AZStd::string varName() const { return m_varName; }
AZStd::string description() const { return m_description; }
void AddProperty(CReflectedVar* property);
void Clear();
//If we're an unnamed container, just show our children in flat list. Otherwise show the container name with children underneath
AZ::u32 GetVisibility() const
{
return m_varName.empty() ? AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20) : AZ_CRC("PropertyVisibility_Show", 0xa43c82dd);
}
void SetAutoExpand(bool autoExpand) { m_autoExpand = autoExpand; }
bool AutoExpand() const { return m_autoExpand; }
AZStd::vector<CReflectedVar*> GetProperties() const { return m_properties; }
void SetValueText(const AZStd::string& valueText) { m_valueText = valueText; }
friend class ReflectedVarInit;
private:
AZStd::vector<CReflectedVar*> m_properties;
bool m_autoExpand = false;
AZStd::string m_valueText;
};
template<class T>
class CReflectedVarAny
: public CReflectedVar
{
public:
AZ_RTTI((CReflectedVarAny<T>, "{EE8293C3-9B1E-470B-9922-2CBB8DA13D78}", T), CReflectedVar)
CReflectedVarAny(const AZStd::string& name, const T& val = T())
: CReflectedVar(name)
, m_value(val) {}
CReflectedVarAny() = default;
AZStd::string varName() const { return m_varName; }
AZStd::string description() const { return m_description; }
static void reflect(AZ::SerializeContext* serializeContext);
T m_value;
};
// Class to hold values that have min/max
// T = data type held in this variable
// R = data type of the range
template<class T, class R>
class CReflectedVarRanged
: public CReflectedVar
{
public:
AZ_RTTI((CReflectedVarRanged, "{6AB4EC29-E17B-4B3B-A153-BFDAA48B8CF8}", T, R), CReflectedVar)
CReflectedVarRanged(const AZStd::string& name, const T& val = T())
: CReflectedVar(name)
, m_value(val)
, m_minVal(std::numeric_limits<R>::lowest())
, m_maxVal(std::numeric_limits<R>::max())
, m_stepSize(1)
, m_softMinVal(std::numeric_limits<R>::lowest())
, m_softMaxVal(std::numeric_limits<R>::max())
{}
CReflectedVarRanged()
: CReflectedVarRanged(AZStd::string(), T()){}
AZStd::string varName() const { return m_varName; }
AZStd::string description() const { return m_description; }
R minValue() const { return m_minVal; }
R maxValue() const { return m_maxVal; }
R stepSize() const { return m_stepSize; }
R softMinVal() const { return m_softMinVal; }
R softMaxVal() const { return m_softMaxVal; }
static void reflect(AZ::SerializeContext* serializeContext);
T m_value;
R m_minVal;
R m_maxVal;
R m_stepSize;
R m_softMinVal;
R m_softMaxVal;
};
//name some commonly-used variable types
template <class T>
using CReflectedVarNumeric = CReflectedVarRanged<T, T>;
//ePropertyFloat
using CReflectedVarFloat = CReflectedVarNumeric<float>;
//ePropertyInt
using CReflectedVarInt = CReflectedVarNumeric<int>;
//ePropertyString
using CReflectedVarString = CReflectedVarAny<AZStd::string>;
//ePropertyBool
using CReflectedVarBool = CReflectedVarAny<bool>;
//ePropertyVector2
using CReflectedVarVector2 = CReflectedVarRanged<AZ::Vector2, float>;
//ePropertyVector
using CReflectedVarVector3 = CReflectedVarRanged<AZ::Vector3, float>;
//ePropertyVector4
using CReflectedVarVector4 = CReflectedVarRanged<AZ::Vector4, float>;
// Class for holding enumerated values, ePropertySelection
// Keeps a key-value pair values (int, string, float, etc) and names corresponding to each value
// The names are displayed to user when editing, the values are used by underlying code.
template<class T>
class CReflectedVarEnum
: public CReflectedVar
{
public:
AZ_RTTI((CReflectedVarEnum<T>, "{40AE7D74-7E3A-41A9-8F71-2BBC3067118B}", T), CReflectedVar)
CReflectedVarEnum(const AZStd::string& name)
: CReflectedVar(name) {}
CReflectedVarEnum() = default;
void setEnums(const AZStd::vector<AZStd::pair<T, AZStd::string> >& enums)
{
m_enums = enums;
if (m_enums.size() > 0)
{
m_value = m_enums.at(0).first;
m_selectedEnumName = m_enums.at(0).second;
}
else
{
m_value = T();
m_selectedEnumName.clear();
}
}
void addEnum(const T& value, const AZStd::string& name)
{
m_enums.push_back(AZStd::pair<T, AZStd::string>(value, name));
if (m_enums.size() == 1)
{
m_selectedEnumName = name;
m_value = value;
}
}
void setEnumValue(const T& value)
{
auto it = std::find_if(m_enums.cbegin(), m_enums.cend(), [value](const AZStd::pair<T, AZStd::string>& item) -> bool { return item.first == value; });
if (it != m_enums.end())
{
m_value = it->first;
m_selectedEnumName = it->second;
}
}
void setEnumByName(const AZStd::string& name)
{
auto it = std::find_if(m_enums.cbegin(), m_enums.cend(), [name](const AZStd::pair<T, AZStd::string>& item) -> bool { return item.second == name; });
if (it != m_enums.end())
{
m_value = it->first;
m_selectedEnumName = it->second;
}
}
void OnEnumChanged()
{
setEnumByName(m_selectedEnumName);
}
AZStd::vector < AZStd::string> GetEnums() const
{
AZStd::vector < AZStd::string> returnVal;
for (const auto& i : m_enums)
{
returnVal.push_back(i.second);
}
return returnVal;
}
AZStd::string varName() const { return m_varName; }
AZStd::string description() const { return m_description; }
static void reflect(AZ::SerializeContext* serializeContext);
T m_value;
AZStd::string m_selectedEnumName;
AZStd::vector<AZStd::pair<T, AZStd::string> > m_enums;
};
//Class to hold ePropertyColor (IVariable::DT_COLOR)
class CReflectedVarColor
: public CReflectedVar
{
public:
AZ_RTTI(CReflectedVarColor, "{CC69E773-B4FA-4B6D-8A46-0B580097B6D2}", CReflectedVar)
CReflectedVarColor(const AZStd::string& name, AZ::Vector3 color = AZ::Vector3())
: CReflectedVar(name)
, m_color(color) {}
CReflectedVarColor() {}
AZStd::string varName() const { return m_varName; }
AZStd::string description() const { return m_description; }
AZ::Vector3 m_color;
};
//Class to hold ePropertyAnimation (IVariable::DT_ANIMATION )
class CReflectedVarAnimation
: public CReflectedVar
{
public:
AZ_RTTI(CReflectedVarAnimation, "{635D982E-23EC-463F-8F33-4FC2C19D5673}", CReflectedVar)
CReflectedVarAnimation(const AZStd::string& name)
: CReflectedVar(name)
, m_entityID(0)
{}
CReflectedVarAnimation()
: m_entityID(0){}
AZStd::string varName() const { return m_varName; }
AZStd::string description() const { return m_description; }
AZStd::string m_animation;
AZ::EntityId m_entityID;
};
//Class to hold:
// ePropertyTexture (IVariable::DT_TEXTURE)
// ePropertyMaterial (IVariable::DT_MATERIAL)
// ePropertyModel (IVariable::DT_OBJECT)
// ePropertyGeomCache (IVariable::DT_GEOM_CACHE)
// ePropertyAudioTrigger (IVariable::DT_AUDIO_TRIGGER)
// ePropertyAudioSwitch (IVariable::DT_AUDIO_SWITCH )
// ePropertyAudioSwitchState (IVariable::DT_AUDIO_SWITCH_STATE)
// ePropertyAudioRTPC (IVariable::DT_AUDIO_RTPC)
// ePropertyAudioEnvironment (IVariable::DT_AUDIO_ENVIRONMENT)
// ePropertyAudioPreloadRequest (IVariable::DT_AUDIO_PRELOAD_REQUEST)
class CReflectedVarResource
: public CReflectedVar
{
public:
AZ_RTTI(CReflectedVarResource, "{162864C2-0C3E-4B6A-84D3-BBAD975B4FD2}", CReflectedVar)
CReflectedVarResource(const AZStd::string& name)
: CReflectedVar(name)
, m_propertyType(ePropertyInvalid)
{}
CReflectedVarResource()
: m_propertyType(ePropertyInvalid){}
AZStd::string varName() const { return m_varName; }
AZStd::string description() const { return m_description; }
AZStd::string m_path;
PropertyType m_propertyType;
};
//Class to hold ePropertyUser (IVariable::DT_USERITEMCB)
class CReflectedVarUser
: public CReflectedVar
{
public:
AZ_RTTI(CReflectedVarUser, "{A901DA91-3893-4848-9AE8-62C0ED074970}", CReflectedVar)
CReflectedVarUser(const AZStd::string &name)
: CReflectedVar(name)
, m_enableEdit(false)
, m_useTree(false)
{}
CReflectedVarUser() : m_enableEdit(false), m_useTree(false) {}
AZStd::string varName() const { return m_varName; }
AZStd::string m_value;
bool m_enableEdit;
bool m_useTree;
AZStd::string m_dialogTitle;
AZStd::string m_treeSeparator;
AZStd::vector<AZStd::string> m_itemNames;
AZStd::vector<AZStd::string> m_itemDescriptions;
};
//Class to hold ePropertyAnimation (IVariable::DT_ANIMATION )
class CReflectedVarSpline
: public CReflectedVar
{
public:
AZ_RTTI(CReflectedVarSpline, "{9A928683-7C84-48BF-8A2E-F7BEC423EE4E}", CReflectedVar)
CReflectedVarSpline(PropertyType propertyType, const AZStd::string &name)
: CReflectedVar(name)
, m_spline(0)
, m_propertyType(propertyType)
{}
CReflectedVarSpline()
: m_spline(0)
, m_propertyType(ePropertyInvalid)
{}
AZStd::string varName() const { return m_varName; }
AZ::u32 handler();
uint64_t m_spline;
PropertyType m_propertyType;
};
//Class to wrap all the many properties that can be represented by a string and edited via a popup
class CReflectedVarGenericProperty
: public CReflectedVar
{
public:
AZ_RTTI(CReflectedVarGenericProperty, "{C4A34C95-3D71-40CE-86D2-DDE314B33CC5}", CReflectedVar)
CReflectedVarGenericProperty(PropertyType pType, const AZStd::string& name = AZStd::string(), const AZStd::string& val = AZStd::string())
: CReflectedVar(name)
, m_propertyType(pType)
, m_value(val) {}
CReflectedVarGenericProperty()
: CReflectedVar()
, m_propertyType(ePropertyInvalid){}
AZStd::string varName() const { return m_varName; }
AZStd::string description() const { return m_description; }
PropertyType propertyType() const { return m_propertyType; }
AZ::u32 handler();
static void reflect(AZ::SerializeContext* serializeContext);
PropertyType m_propertyType;
AZStd::string m_value;
};
class EDITOR_CORE_API ReflectedVarInit
{
public:
static void setupReflection(AZ::SerializeContext* serializeContext);
private:
static bool s_reflectionDone;
};
//Class to hold ePropertyMotion (IVariable::DT_MOTION )
class CReflectedVarMotion
: public CReflectedVar
{
public:
AZ_RTTI(CReflectedVarMotion, "{66397EFB-620A-40B8-8C66-D6AECF690DF5}", CReflectedVar)
CReflectedVarMotion(const AZStd::string& name)
: CReflectedVar(name)
, m_assetId(0) {}
CReflectedVarMotion()
: m_assetId(0) {}
AZStd::string varName() const { return m_varName; }
AZStd::string description() const { return m_description; }
AZStd::string m_motion;
AZ::Data::AssetId m_assetId;
};
#endif // CRYINCLUDE_EDITOR_UTILS_REFLECTEDVAR_H
@@ -0,0 +1,586 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 "EditorDefs.h"
#include "ReflectedVarWrapper.h"
// AzCore
#include <AzCore/Asset/AssetManagerBus.h>
// Editor
#include "ReflectedPropertyCtrl.h"
#include "UIEnumsDatabase.h"
namespace {
//setting the IVariable to itself in property items was trigger to update limits for that variable.
//limits were obtained using IVariable::GetLimits instead of from the Prop::Description
template <class T, class R>
void setRangeParams(CReflectedVarRanged<T, R> *reflectedVar, IVariable *pVariable, bool updatingExistingVariable = false)
{
float min, max, step;
bool hardMin, hardMax;
if (updatingExistingVariable)
{
pVariable->GetLimits(min, max, step, hardMin, hardMax);
}
else
{
Prop::Description desc(pVariable);
min = desc.m_rangeMin;
max = desc.m_rangeMax;
step = desc.m_step;
hardMin = desc.m_bHardMin;
hardMax = desc.m_bHardMax;
}
reflectedVar->m_softMinVal = min;
reflectedVar->m_softMaxVal = max;
if (hardMin)
{
reflectedVar->m_minVal = min;
}
else
{
reflectedVar->m_minVal = std::numeric_limits<int>::lowest();
}
if (hardMax)
{
reflectedVar->m_maxVal = max;
}
else
{
// There is an issue with assigning std::numeric_limits<int>::max() to a float
// A float can't actually represent the value of 2147483647 and clang
// compilers actually warn on this fact.
// A static_cast is used here to indicate explicit acceptance of the value change here
/* The clang compiler warning is below
../Code/Sandbox/Editor/Controls/ReflectedPropertyControl/ReflectedVarWrapper.cpp:59:38: error: implicit conversion from 'int' to 'float' changes value from 2147483647 to 2147483648 [-Werror,-Wimplicit-int-float-conversion]
reflectedVar->m_maxVal = std::numeric_limits<int>::max();
*/
reflectedVar->m_maxVal = static_cast<float>(std::numeric_limits<int>::max());
}
reflectedVar->m_stepSize = step;
}
}
void ReflectedVarIntAdapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarInt(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
UpdateRangeLimits(pVariable);
Prop::Description desc(pVariable);
m_valueMultiplier = desc.m_valueMultiplier;
}
void ReflectedVarIntAdapter::UpdateRangeLimits(IVariable *pVariable)
{
setRangeParams<int>(m_reflectedVar.data(), pVariable);
}
void ReflectedVarIntAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
float value;
if (pVariable->GetType() == IVariable::FLOAT)
{
pVariable->Get(value);
}
else
{
int intValue;
pVariable->Get(intValue);
value = intValue;
}
m_reflectedVar->m_value = std::round(value * m_valueMultiplier);
}
void ReflectedVarIntAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
//don't round here. Often the IVariable is actually a float under-the hood
//for example: DT_PERCENT is stored in float (0 to 1) but has ePropertyType::Integer because editor should be an integer editor ranging from 0 to 100.
pVariable->Set(m_reflectedVar->m_value / m_valueMultiplier);
}
void ReflectedVarFloatAdapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarFloat(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
UpdateRangeLimits(pVariable);
Prop::Description desc(pVariable);
m_valueMultiplier = desc.m_valueMultiplier;
}
void ReflectedVarFloatAdapter::UpdateRangeLimits(IVariable *pVariable)
{
setRangeParams<float>(m_reflectedVar.data(), pVariable);
}
void ReflectedVarFloatAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
float value;
pVariable->Get(value);
m_reflectedVar->m_value = value * m_valueMultiplier;
}
void ReflectedVarFloatAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
pVariable->Set(m_reflectedVar->m_value/m_valueMultiplier);
}
void ReflectedVarStringAdapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarString(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
}
void ReflectedVarStringAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
QString value;
pVariable->Get(value);
m_reflectedVar->m_value = value.toUtf8().data();
}
void ReflectedVarStringAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
pVariable->Set(m_reflectedVar->m_value.c_str());
}
void ReflectedVarBoolAdapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarBool(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
}
void ReflectedVarBoolAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
bool value;
pVariable->Get(value);
m_reflectedVar->m_value = value;
}
void ReflectedVarBoolAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
pVariable->Set(m_reflectedVar->m_value);
}
ReflectedVarEnumAdapter::ReflectedVarEnumAdapter()
: m_updatingEnums(false)
, m_pVariable(nullptr)
{}
void ReflectedVarEnumAdapter::SetVariable(IVariable *pVariable)
{
m_pVariable = pVariable;
Prop::Description desc(pVariable);
m_enumList = desc.m_enumList;
m_reflectedVar.reset(new CReflectedVarEnum<AZStd::string>(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
UpdateReflectedVarEnums();
}
bool ReflectedVarEnumAdapter::UpdateReflectedVarEnums()
{
if (!m_pVariable || m_updatingEnums)
{
return false;
}
m_updatingEnums = true;
//Allow derived classes to populate the IVariable's enumList
updateIVariableEnumList(m_pVariable);
m_enumList = m_pVariable->GetEnumList();
m_updatingEnums = false;
bool changed = false;
//Copy the updated enums to the ReflecteVar
if (m_enumList)
{
const AZStd::vector<AZStd::string> oldEnums = m_reflectedVar->GetEnums();
AZStd::vector<AZStd::pair<AZStd::string, AZStd::string>> enums;
for (uint i = 0; !m_enumList->GetItemName(i).isNull(); i++)
{
QString sEnumName = m_enumList->GetItemName(i);
enums.push_back(AZStd::pair<AZStd::string, AZStd::string>(sEnumName.toUtf8().data(), sEnumName.toUtf8().data()));
}
m_reflectedVar->setEnums(enums);
changed = m_reflectedVar->GetEnums() != oldEnums;
if (changed)
{
// set the current enum value from the IVariable
SyncReflectedVarToIVar(m_pVariable);
}
}
return changed;
}
void ReflectedVarEnumAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
const AZStd::string value = pVariable->GetDisplayValue().toUtf8().data();
m_reflectedVar->setEnumByName(value);
}
void ReflectedVarEnumAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
QString iVarVal = m_reflectedVar->m_selectedEnumName.c_str();
pVariable->SetDisplayValue(iVarVal);
}
void ReflectedVarEnumAdapter::OnVariableChange([[maybe_unused]] IVariable* pVariable)
{
//setting the enums on the pVariable will cause the variable to change getting us back here
//The original property editor did need to update things immediately because it did so when creating the in-place editing control
if (!m_updatingEnums)
{
UpdateReflectedVarEnums();
}
}
void ReflectedVarDBEnumAdapter::SetVariable(IVariable *pVariable)
{
Prop::Description desc(pVariable);
m_pEnumDBItem = desc.m_pEnumDBItem;
m_reflectedVar.reset(new CReflectedVarEnum<AZStd::string>(pVariable->GetHumanName().toUtf8().data()));
if (m_pEnumDBItem)
{
for (int i = 0; i < m_pEnumDBItem->strings.size(); i++)
{
QString name = m_pEnumDBItem->strings[i];
m_reflectedVar->addEnum( m_pEnumDBItem->NameToValue(name).toUtf8().data(), name.toUtf8().data() );
}
}
}
void ReflectedVarDBEnumAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
const AZStd::string valueStr = pVariable->GetDisplayValue().toUtf8().data();
const AZStd::string value = m_pEnumDBItem ? AZStd::string(m_pEnumDBItem->ValueToName(valueStr.c_str()).toUtf8().data()) : valueStr;
m_reflectedVar->setEnumByName(value);
}
void ReflectedVarDBEnumAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
QString iVarVal = m_reflectedVar->m_selectedEnumName.c_str();
if (m_pEnumDBItem)
{
iVarVal = m_pEnumDBItem->NameToValue(iVarVal);
}
pVariable->SetDisplayValue(iVarVal);
}
void ReflectedVarVector2Adapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarVector2(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
UpdateRangeLimits(pVariable);
}
void ReflectedVarVector2Adapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
Vec2 vec;
pVariable->Get(vec);
m_reflectedVar->m_value = AZ::Vector2(vec.x, vec.y);
}
void ReflectedVarVector2Adapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
pVariable->Set(Vec2(m_reflectedVar->m_value.GetX(), m_reflectedVar->m_value.GetY()));
}
void ReflectedVarVector3Adapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarVector3(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
UpdateRangeLimits(pVariable);
}
void ReflectedVarVector3Adapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
Vec3 vec;
pVariable->Get(vec);
m_reflectedVar->m_value = AZ::Vector3(vec.x, vec.y, vec.z);
}
void ReflectedVarVector3Adapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
pVariable->Set(Vec3(m_reflectedVar->m_value.GetX(), m_reflectedVar->m_value.GetY(), m_reflectedVar->m_value.GetZ()));
}
void ReflectedVarVector4Adapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarVector4(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
UpdateRangeLimits(pVariable);
}
void ReflectedVarVector4Adapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
Vec4 vec;
pVariable->Get(vec);
m_reflectedVar->m_value = AZ::Vector4(vec.x, vec.y, vec.z, vec.w);
}
void ReflectedVarVector4Adapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
pVariable->Set(Vec4(m_reflectedVar->m_value.GetX(), m_reflectedVar->m_value.GetY(), m_reflectedVar->m_value.GetZ(), m_reflectedVar->m_value.GetW()));
}
void ReflectedVarColorAdapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarColor(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
}
void ReflectedVarColorAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
if (pVariable->GetType() == IVariable::VECTOR)
{
Vec3 v(0, 0, 0);
pVariable->Get(v);
const QColor col = ColorLinearToGamma(ColorF(v.x, v.y, v.z));
m_reflectedVar->m_color.Set(col.redF(), col.greenF(), col.blueF());
}
else
{
int col(0);
pVariable->Get(col);
const QColor qcolor = ColorToQColor((uint32)col);
m_reflectedVar->m_color.Set(qcolor.redF(), qcolor.greenF(), qcolor.blueF());
}
}
void ReflectedVarColorAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
if (pVariable->GetType() == IVariable::VECTOR)
{
ColorF colLin = ColorGammaToLinear(QColor::fromRgbF(m_reflectedVar->m_color.GetX(), m_reflectedVar->m_color.GetY(), m_reflectedVar->m_color.GetZ()));
pVariable->Set(Vec3(colLin.r, colLin.g, colLin.b));
}
else
{
int ir = m_reflectedVar->m_color.GetX() * 255.0f;
int ig = m_reflectedVar->m_color.GetY() * 255.0f;
int ib = m_reflectedVar->m_color.GetZ() * 255.0f;
pVariable->Set(static_cast<int>(RGB(ir, ig, ib)));
}
}
void ReflectedVarAnimationAdapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarAnimation(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
}
void ReflectedVarAnimationAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
m_reflectedVar->m_entityID = static_cast<AZ::EntityId>(pVariable->GetUserData().value<AZ::u64>());
m_reflectedVar->m_animation = pVariable->GetDisplayValue().toUtf8().data();
}
void ReflectedVarAnimationAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
pVariable->SetUserData(static_cast<AZ::u64>(m_reflectedVar->m_entityID));
pVariable->SetDisplayValue(m_reflectedVar->m_animation.c_str());
}
void ReflectedVarResourceAdapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarResource(pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
}
void ReflectedVarResourceAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
QString path;
pVariable->Get(path);
m_reflectedVar->m_path = path.toUtf8().data();
Prop::Description desc(pVariable);
m_reflectedVar->m_propertyType = desc.m_type;
}
void ReflectedVarResourceAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
const bool bForceModified = (m_reflectedVar->m_propertyType == ePropertyGeomCache);
pVariable->SetForceModified(bForceModified);
pVariable->SetDisplayValue(m_reflectedVar->m_path.c_str());
//shouldn't be able to change the type, so ignore m_reflecatedVar->m_properyType
}
ReflectedVarGenericPropertyAdapter::ReflectedVarGenericPropertyAdapter(PropertyType propertyType)
:m_propertyType(propertyType)
{}
void ReflectedVarGenericPropertyAdapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarGenericProperty(m_propertyType, pVariable->GetHumanName().toUtf8().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toUtf8().data();
}
void ReflectedVarGenericPropertyAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
QString value;
pVariable->Get(value);
if (m_reflectedVar->m_propertyType == ePropertyMaterial)
value.replace('\\', '/');
m_reflectedVar->m_value = value.toUtf8().data();
}
void ReflectedVarGenericPropertyAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
pVariable->Set(m_reflectedVar->m_value.c_str());
}
void ReflectedVarUserAdapter::SetVariable(IVariable *pVariable)
{
m_reflectedVar.reset(new CReflectedVarUser( pVariable->GetHumanName().toUtf8().data()));
}
void ReflectedVarUserAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
QString value;
pVariable->Get(value);
m_reflectedVar->m_value = value.toUtf8().data();
//extract the list of custom items from the IVariable user data
IVariable::IGetCustomItems* pGetCustomItems = static_cast<IVariable::IGetCustomItems*> (pVariable->GetUserData().value<void *>());
if (pGetCustomItems != 0)
{
std::vector<IVariable::IGetCustomItems::SItem> items;
QString dlgTitle;
// call the user supplied callback to fill-in items and get dialog title
bool bShowIt = pGetCustomItems->GetItems(pVariable, items, dlgTitle);
if (bShowIt) // if func didn't veto, show the dialog
{
m_reflectedVar->m_enableEdit = true;
m_reflectedVar->m_useTree = pGetCustomItems->UseTree();
m_reflectedVar->m_treeSeparator = pGetCustomItems->GetTreeSeparator();
m_reflectedVar->m_dialogTitle = dlgTitle.toUtf8().data();
m_reflectedVar->m_itemNames.resize(items.size());
m_reflectedVar->m_itemDescriptions.resize(items.size());
QByteArray ba;
int i = -1;
std::generate(m_reflectedVar->m_itemNames.begin(), m_reflectedVar->m_itemNames.end(), [&items, &i, &ba]() { ++i; ba = items[i].name.toUtf8(); return ba.data(); });
i = -1;
std::generate(m_reflectedVar->m_itemDescriptions.begin(), m_reflectedVar->m_itemDescriptions.end(), [&items, &i, &ba]() { ++i; ba = items[i].desc.toUtf8(); return ba.data(); });
}
}
else
{
m_reflectedVar->m_enableEdit = false;
}
}
void ReflectedVarUserAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
pVariable->Set(m_reflectedVar->m_value.c_str());
}
ReflectedVarSplineAdapter::ReflectedVarSplineAdapter(ReflectedPropertyItem *parentItem, PropertyType propertyType)
: m_propertyType(propertyType)
, m_bDontSendToControl(false)
, m_parentItem(parentItem)
{
}
void ReflectedVarSplineAdapter::SetVariable(IVariable* pVariable)
{
m_reflectedVar.reset(new CReflectedVarSpline(m_propertyType, pVariable->GetHumanName().toUtf8().data()));
}
void ReflectedVarSplineAdapter::SyncReflectedVarToIVar(IVariable* pVariable)
{
if (!m_bDontSendToControl)
{
m_reflectedVar->m_spline = reinterpret_cast<uint64_t>(pVariable->GetSpline());
}
}
void ReflectedVarSplineAdapter::SyncIVarToReflectedVar(IVariable* pVariable)
{
// Splines update variables directly so don't call OnVariableChange or SetValue here or values will be overwritten.
// Call OnSetValue to force this field to notify this variable that its model has changed without going through the
// full OnVariableChange pass
//
// Set m_bDontSendToControl to prevent the control's data from being overwritten (as the variable's data won't
// necessarily be up to date vs the controls at the point this happens).
m_bDontSendToControl = true;
pVariable->OnSetValue(false);
m_bDontSendToControl = false;
m_parentItem->SendOnItemChange();
}
void ReflectedVarMotionAdapter::SetVariable(IVariable *pVariable)
{
// Create new reflected var
m_reflectedVar.reset(new CReflectedVarMotion(pVariable->GetHumanName().toLatin1().data()));
m_reflectedVar->m_description = pVariable->GetDescription().toLatin1().data();
// Set the asset id
AZStd::string stringGuid = pVariable->GetDisplayValue().toLatin1().data();
AZ::Uuid guid(stringGuid.c_str(), stringGuid.length());
AZ::u32 subId = pVariable->GetUserData().value<AZ::u32>();
m_reflectedVar->m_assetId = AZ::Data::AssetId(guid, subId);
// Lookup Filename by assetId and get the filename part of the description
EBUS_EVENT_RESULT(m_reflectedVar->m_motion, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, m_reflectedVar->m_assetId);
}
void ReflectedVarMotionAdapter::SyncReflectedVarToIVar(IVariable *pVariable)
{
AZStd::string stringGuid = pVariable->GetDisplayValue().toLatin1().data();
AZ::Uuid guid(stringGuid.c_str(), stringGuid.length());
AZ::u32 subId = pVariable->GetUserData().value<AZ::u32>();
m_reflectedVar->m_assetId = AZ::Data::AssetId(guid, subId);
// Lookup Filename by assetId and get the filename part of the description
EBUS_EVENT_RESULT(m_reflectedVar->m_motion, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, m_reflectedVar->m_assetId);
}
void ReflectedVarMotionAdapter::SyncIVarToReflectedVar(IVariable *pVariable)
{
pVariable->SetUserData(m_reflectedVar->m_assetId.m_subId);
pVariable->SetDisplayValue(m_reflectedVar->m_assetId.m_guid.ToString<AZStd::string>().c_str());
}
@@ -0,0 +1,321 @@
/*
* 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.
*
*/
#ifndef CRYINCLUDE_EDITOR_UTILS_REFLECTEDVARWRAPPER_H
#define CRYINCLUDE_EDITOR_UTILS_REFLECTEDVARWRAPPER_H
#pragma once
#include "Util/Variable.h"
#include <Util/VariablePropertyType.h>
#include "ReflectedVar.h"
#include <QScopedPointer>
struct CUIEnumsDatabase_SEnum;
class ReflectedPropertyItem;
// Class to wrap the CReflectedVars and sync them with corresponding IVariable.
// Most of this code is ported from CPropertyItem functions that marshal data between
// IVariable and editor widgets.
class EDITOR_CORE_API ReflectedVarAdapter
{
public:
virtual ~ReflectedVarAdapter(){};
// update the range limits in CReflectedVar to range specified in IVariable
virtual void UpdateRangeLimits([[maybe_unused]] IVariable* pVariable) {};
//set IVariable for this property and create a CReflectedVar to represent it
virtual void SetVariable(IVariable* pVariable) = 0;
//update the ReflectedVar to current value of IVar
virtual void SyncReflectedVarToIVar(IVariable* pVariable) = 0;
//update the internal IVariable as result of ReflectedVar changing
virtual void SyncIVarToReflectedVar(IVariable* pVariable) = 0;
// Callback called when variable change. SyncReflectedVarToIVar will be called after
virtual void OnVariableChange([[maybe_unused]] IVariable* var) {};
virtual bool UpdateReflectedVarEnums() { return false; }
virtual CReflectedVar* GetReflectedVar() = 0;
//needed for containers that can have new values filled in
virtual void ReplaceVarBlock([[maybe_unused]] CVarBlock* varBlock) {};
virtual bool Contains(CReflectedVar* var) { return GetReflectedVar() == var; }
};
class EDITOR_CORE_API ReflectedVarIntAdapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void UpdateRangeLimits(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarInt > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
float m_valueMultiplier = 1.0f;
};
class EDITOR_CORE_API ReflectedVarFloatAdapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void UpdateRangeLimits(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarFloat > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
float m_valueMultiplier = 1.0f;
};
class EDITOR_CORE_API ReflectedVarStringAdapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarString > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
class EDITOR_CORE_API ReflectedVarBoolAdapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarBool > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
class EDITOR_CORE_API ReflectedVarEnumAdapter
: public ReflectedVarAdapter
{
public:
ReflectedVarEnumAdapter();
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
virtual void OnVariableChange(IVariable* var);
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
protected:
//update the ReflectedVar with the allowable enum options
bool UpdateReflectedVarEnums() override;
//virtual function to allow derived classes to update the enum list before syncing with ReflectedVar.
virtual void updateIVariableEnumList([[maybe_unused]] IVariable* pVariable) {};
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarEnum<AZStd::string> > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
IVariable* m_pVariable;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
IVarEnumListPtr m_enumList;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
bool m_updatingEnums;
};
class EDITOR_CORE_API ReflectedVarDBEnumAdapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarEnum<AZStd::string> > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
CUIEnumsDatabase_SEnum* m_pEnumDBItem;
};
class EDITOR_CORE_API ReflectedVarVector2Adapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarVector2 > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
class EDITOR_CORE_API ReflectedVarVector3Adapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarVector3 > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
class EDITOR_CORE_API ReflectedVarVector4Adapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarVector4 > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
class EDITOR_CORE_API ReflectedVarColorAdapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarColor > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
class EDITOR_CORE_API ReflectedVarAnimationAdapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarAnimation > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
class EDITOR_CORE_API ReflectedVarResourceAdapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarResource> m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
class EDITOR_CORE_API ReflectedVarUserAdapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable *pVariable) override;
void SyncReflectedVarToIVar(IVariable *pVariable) override;
void SyncIVarToReflectedVar(IVariable *pVariable) override;
CReflectedVar *GetReflectedVar() override {
return m_reflectedVar.data();
}
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarUser> m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
class EDITOR_CORE_API ReflectedVarSplineAdapter
: public ReflectedVarAdapter
{
public:
ReflectedVarSplineAdapter(ReflectedPropertyItem *parentItem, PropertyType propertyType);
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override {
return m_reflectedVar.data();
}
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarSpline > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
bool m_bDontSendToControl;
PropertyType m_propertyType;
ReflectedPropertyItem *m_parentItem;
};
class EDITOR_CORE_API ReflectedVarGenericPropertyAdapter
: public ReflectedVarAdapter
{
public:
ReflectedVarGenericPropertyAdapter(PropertyType propertyType);
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarGenericProperty > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
PropertyType m_propertyType;
};
class EDITOR_CORE_API ReflectedVarMotionAdapter
: public ReflectedVarAdapter
{
public:
void SetVariable(IVariable* pVariable) override;
void SyncReflectedVarToIVar(IVariable* pVariable) override;
void SyncIVarToReflectedVar(IVariable* pVariable) override;
CReflectedVar* GetReflectedVar() override { return m_reflectedVar.data(); }
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<CReflectedVarMotion > m_reflectedVar;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
#endif // CRYINCLUDE_EDITOR_UTILS_REFLECTEDVARWRAPPER_H
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1e7fb0db541a13f6f6b6f494f133f72e05c847508bdf69e1bc63dcf8eb4aa752
size 311
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:124de9138d3af8e14a49944245d7245df4065c49ad1e5ec1d8377600933558c5
size 370
+843
View File
@@ -0,0 +1,843 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "SplineCtrl.h"
// Qt
#include <QPainter>
#include <QPainterPath>
#include <QToolTip>
// Editor
#include "TimelineCtrl.h"
#define MIN_TIME_EPSILON 0.01f
//////////////////////////////////////////////////////////////////////////
CSplineCtrl::CSplineCtrl(QWidget* parent)
: QWidget(parent)
{
m_nActiveKey = -1;
m_nHitKeyIndex = -1;
m_nKeyDrawRadius = 3;
m_bTracking = false;
m_pSpline = 0;
m_gridX = 10;
m_gridY = 10;
m_fMinTime = -1;
m_fMaxTime = 1;
m_fMinValue = -1;
m_fMaxValue = 1;
m_fTooltipScaleX = 1;
m_fTooltipScaleY = 1;
m_bLockFirstLastKey = false;
m_pTimelineCtrl = 0;
m_bSelectedKeys.reserve(0);
m_fTimeMarker = -10;
setMouseTracking(true);
}
CSplineCtrl::~CSplineCtrl()
{
}
/////////////////////////////////////////////////////////////////////////////
// CSplineCtrl message handlers
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::resizeEvent([[maybe_unused]] QResizeEvent* event)
{
m_rcSpline = rect();
if (m_pTimelineCtrl)
{
QRect rct = m_rcSpline;
rct.setHeight(20);
m_rcSpline.setTop(rct.bottom() + 1);
m_pTimelineCtrl->setGeometry(rct);
}
m_rcSpline.adjust(2, 2, -2, -2);
}
//////////////////////////////////////////////////////////////////////////
QPoint CSplineCtrl::KeyToPoint(int nKey)
{
if (nKey >= 0)
{
return TimeToPoint(m_pSpline->GetKeyTime(nKey));
}
return QPoint(0, 0);
}
//////////////////////////////////////////////////////////////////////////
QPoint CSplineCtrl::TimeToPoint(float time)
{
QPoint point;
point.setX((time - m_fMinTime) * (m_rcSpline.width() / (m_fMaxTime - m_fMinTime)) + m_rcSpline.left());
float val = 0;
if (m_pSpline)
{
m_pSpline->InterpolateFloat(time, val);
}
point.setY((floor((m_fMaxValue - val) * (m_rcSpline.height() / (m_fMaxValue - m_fMinValue)) + 0.5f) + m_rcSpline.top()));
return point;
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::PointToTimeValue(const QPoint& point, float& time, float& value)
{
time = XOfsToTime(point.x());
float t = float(m_rcSpline.bottom() - point.y()) / m_rcSpline.height();
value = LERP(m_fMinValue, m_fMaxValue, t);
}
//////////////////////////////////////////////////////////////////////////
float CSplineCtrl::XOfsToTime(int x)
{
// m_fMinTime to m_fMaxTime time range.
float t = float(x - m_rcSpline.left()) / m_rcSpline.width();
return LERP(m_fMinTime, m_fMaxTime, t);
}
//////////////////////////////////////////////////////////////////////////
QPoint CSplineCtrl::XOfsToPoint(int x)
{
return TimeToPoint(XOfsToTime(x));
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
QRect rcClient = rect();
if (m_pSpline)
{
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
}
{
if (m_TimeUpdateRect != event->rect())
{
painter.fillRect(event->rect(), QColor(140, 140, 140));
//Draw Grid
DrawGrid(&painter);
//Draw Keys and Curve
if (m_pSpline)
{
DrawSpline(&painter);
DrawKeys(&painter);
}
}
m_TimeUpdateRect = QRect();
}
DrawTimeMarker(&painter);
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::DrawGrid(QPainter* painter)
{
QPen pOldPen = painter->pen();
int cx = m_rcSpline.width();
int cy = m_rcSpline.height();
QPen pen(QColor(90, 90, 90), 1, Qt::DotLine);
pen.setCosmetic(true);
painter->setPen(pen);
//Draw Vertical Grid Lines
for (int y = 1; y < m_gridX; y++)
{
painter->drawLine(m_rcSpline.left() + y * cx / m_gridX, m_rcSpline.top() + cy, m_rcSpline.left() + y * cx / m_gridX, m_rcSpline.top());
}
//Draw Horizontal Grid Lines
for (int x = 1; x < m_gridY; x++)
{
painter->drawLine(m_rcSpline.left(), m_rcSpline.top() + x * cy / m_gridY, m_rcSpline.left() + cx, m_rcSpline.top() + x * cy / m_gridY);
}
painter->setPen(QColor(75, 75, 75));
painter->drawLine(m_rcSpline.left() + (m_gridX / 2) * cx / m_gridX, m_rcSpline.top() + cy, m_rcSpline.left() + (m_gridX / 2) * cx / m_gridX, m_rcSpline.top() + 0);
painter->drawLine(m_rcSpline.left() + 0, m_rcSpline.left() + (m_gridY / 2) * cy / m_gridY, m_rcSpline.left() + cx, m_rcSpline.left() + (m_gridY / 2) * cy / m_gridY);
painter->drawRect(m_rcSpline);
painter->setPen(pOldPen);
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::DrawSpline(QPainter* painter)
{
int cx = m_rcSpline.width();
int cy = m_rcSpline.height();
//Draw Curve
// create and select a thick, white pen
const QPen pOldPen = painter->pen();
painter->setPen(QColor(128, 255, 128));
const QRect rcClip = painter->hasClipping() ? painter->clipBoundingRect().intersected(m_rcSpline).toRect() : m_rcSpline;
bool bFirst = true;
QPainterPath path;
for (int x = rcClip.left(); x < rcClip.right(); x++)
{
QPoint pt = XOfsToPoint(x);
if (!bFirst)
{
path.lineTo(pt);
}
else
{
path.moveTo(pt);
bFirst = false;
}
}
painter->drawPath(path);
// Put back the old objects
painter->setPen(pOldPen);
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::DrawKeys(QPainter* painter)
{
if (!m_pSpline)
{
return;
}
// create and select a white pen
const QPen pOldPen = painter->pen();
painter->setPen(Qt::black);
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
for (int i = 0; i < m_pSpline->GetKeyCount(); i++)
{
float time = m_pSpline->GetKeyTime(i);
const QPoint pt = TimeToPoint(time);
QColor clr(220, 220, 0);
if (m_bSelectedKeys[i])
{
clr = QColor(255, 0, 0);
}
const QBrush pOldBrush = painter->brush();
painter->setBrush(clr);
// Draw this key.
painter->drawRect(QRect(QPoint(pt.x() - m_nKeyDrawRadius, pt.y() - m_nKeyDrawRadius), QPoint(pt.x() + m_nKeyDrawRadius - 1, pt.y() + m_nKeyDrawRadius - 1)));
painter->setBrush(pOldBrush);
}
painter->setPen(pOldPen);
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::DrawTimeMarker(QPainter* painter)
{
painter->setPen(QColor(255, 0, 255));
const QPoint pt = TimeToPoint(m_fTimeMarker);
painter->drawLine(pt.x(), m_rcSpline.top() + 1, pt.x(), m_rcSpline.bottom() - 1);
}
void CSplineCtrl::UpdateToolTip()
{
if (m_nHitKeyIndex >= 0 && m_pSpline)
{
float time = m_pSpline->GetKeyTime(m_nHitKeyIndex);
float val;
m_pSpline->GetKeyValueFloat(m_nHitKeyIndex, val);
int cont_s = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_IN_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2;
int cont_d = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_OUT_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2;
const QString tipText = tr("%1, %2, [%3|%4").arg(time * m_fTooltipScaleX, 3, 'f').arg(val * m_fTooltipScaleY, 3, 'f').arg(cont_s).arg(cont_d);
QToolTip::showText(QCursor::pos(), tipText, this);
}
}
/////////////////////////////////////////////////////////////////////////////
//Mouse Message Handlers
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::mousePressEvent(QMouseEvent* event)
{
switch (event->button())
{
case Qt::LeftButton:
OnLButtonDown(event->pos(), event->modifiers());
break;
case Qt::RightButton:
OnRButtonDown(event->pos(), event->modifiers());
break;
}
}
void CSplineCtrl::mouseReleaseEvent(QMouseEvent* event)
{
switch (event->button())
{
case Qt::LeftButton:
OnLButtonUp(event->pos(), event->modifiers());
break;
}
}
void CSplineCtrl::OnLButtonDown([[maybe_unused]] const QPoint& point, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
{
if (m_bTracking)
{
return;
}
if (!m_pSpline)
{
return;
}
setFocus();
switch (m_hitCode)
{
case HIT_KEY:
StartTracking();
SetActiveKey(m_nHitKeyIndex);
break;
case HIT_SPLINE:
// Cycle the spline slope of the nearest key.
ToggleKeySlope(m_nHitKeyIndex, m_nHitKeyDist);
SetActiveKey(-1);
break;
case HIT_NOTHING:
SetActiveKey(-1);
break;
}
update();
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::OnRButtonDown([[maybe_unused]] const QPoint& point, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
{
setFocus();
if (!m_pSpline)
{
return;
}
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::mouseDoubleClickEvent(QMouseEvent* event)
{
if (!m_pSpline || event->button() != Qt::LeftButton)
{
return;
}
switch (m_hitCode)
{
case HIT_NOTHING:
{
int iIndex = InsertKey(event->pos());
SetActiveKey(iIndex);
update();
}
break;
case HIT_KEY:
{
RemoveKey(m_nHitKeyIndex);
}
break;
}
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::mouseMoveEvent(QMouseEvent* event)
{
OnSetCursor();
if (!m_pSpline)
{
return;
}
if (m_bTracking)
{
TrackKey(event->pos());
UpdateToolTip();
}
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::OnLButtonUp([[maybe_unused]] const QPoint& point, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
{
if (!m_pSpline)
{
return;
}
if (m_bTracking)
{
StopTracking();
}
}
/////////////////////////////////////////////////////////////////////////////
void CSplineCtrl::SetActiveKey(int nIndex)
{
ClearSelection();
// Activate New Key
if (nIndex >= 0)
{
m_bSelectedKeys[nIndex] = true;
}
m_nActiveKey = nIndex;
update();
}
/////////////////////////////////////////////////////////////////////////////
void CSplineCtrl::SetSpline(ISplineInterpolator* pSpline, BOOL bRedraw)
{
if (pSpline != m_pSpline)
{
m_pSpline = pSpline;
}
ValidateSpline();
ClearSelection();
if (bRedraw)
{
update();
}
}
void CSplineCtrl::ValidateSpline()
{
// Add initial control points (will be serialised only if edited).
if (m_pSpline->GetKeyCount() == 0)
{
m_pSpline->InsertKeyFloat(0.f, 1.f);
m_pSpline->InsertKeyFloat(1.f, 1.f);
m_pSpline->Update();
}
}
//////////////////////////////////////////////////////////////////////////
ISplineInterpolator* CSplineCtrl::GetSpline()
{
return m_pSpline;
}
/////////////////////////////////////////////////////////////////////////////
void CSplineCtrl::OnSetCursor()
{
const QPoint point = mapFromGlobal(QCursor::pos());
const int hitKey = m_nHitKeyIndex;
switch (HitTest(point))
{
case HIT_SPLINE:
{
setCursor(CMFCUtils::LoadCursor(IDC_ARRWHITE));
} break;
case HIT_KEY:
{
setCursor(CMFCUtils::LoadCursor(IDC_ARRBLCK));
} break;
default:
unsetCursor();
break;
}
if (m_bTracking)
{
m_nHitKeyIndex = hitKey;
}
if (m_pSpline)
{
if (m_nHitKeyIndex >= 0)
{
UpdateToolTip();
}
else if (!m_bTracking)
{
QToolTip::hideText();
}
}
}
/////////////////////////////////////////////////////////////////////////////
void CSplineCtrl::keyPressEvent(QKeyEvent* event)
{
bool bProcessed = false;
if (m_nActiveKey != -1 && m_pSpline)
{
switch (event->key())
{
case Qt::Key_Space:
{
ToggleKeySlope(m_nActiveKey, 0);
bProcessed = true;
} break;
case Qt::Key_Delete:
{
RemoveKey(m_nActiveKey);
bProcessed = true;
} break;
case Qt::Key_Up:
{
CUndo undo("Move Spline Key");
QPoint point = KeyToPoint(m_nActiveKey);
point.ry() -= 1;
emit beforeChange();
TrackKey(point);
bProcessed = true;
} break;
case Qt::Key_Down:
{
CUndo undo("Move Spline Key");
QPoint point = KeyToPoint(m_nActiveKey);
point.ry() += 1;
emit beforeChange();
TrackKey(point);
bProcessed = true;
} break;
case Qt::Key_Left:
{
CUndo undo("Move Spline Key");
QPoint point = KeyToPoint(m_nActiveKey);
point.rx() -= 1;
emit beforeChange();
TrackKey(point);
bProcessed = true;
} break;
case Qt::Key_Right:
{
CUndo undo("Move Spline Key");
QPoint point = KeyToPoint(m_nActiveKey);
point.ry() += 1;
emit beforeChange();
TrackKey(point);
bProcessed = true;
} break;
default:
break; //do nothing
}
update();
}
event->setAccepted(bProcessed);
}
//////////////////////////////////////////////////////////////////////////////
CSplineCtrl::EHitCode CSplineCtrl::HitTest(const QPoint& point)
{
if (!m_pSpline)
{
return HIT_NOTHING;
}
float time, val;
PointToTimeValue(point, time, val);
m_nHitKeyIndex = -1;
m_nHitKeyDist = 0xFFFF;
m_hitCode = HIT_NOTHING;
QPoint splinePt = TimeToPoint(time);
if (abs(splinePt.y() - point.y()) < 4)
{
m_hitCode = HIT_SPLINE;
for (int i = 0; i < m_pSpline->GetKeyCount(); i++)
{
const QPoint splinePt2 = TimeToPoint(m_pSpline->GetKeyTime(i));
if (abs(point.x() - splinePt2.x()) < abs(m_nHitKeyDist))
{
m_nHitKeyIndex = i;
m_nHitKeyDist = point.x() - splinePt2.x();
}
}
if (abs(m_nHitKeyDist) < 4)
{
m_hitCode = HIT_KEY;
}
}
return m_hitCode;
}
///////////////////////////////////////////////////////////////////////////////
void CSplineCtrl::StartTracking()
{
m_bTracking = TRUE;
GetIEditor()->BeginUndo();
emit beforeChange();
setCursor(CMFCUtils::LoadCursor(IDC_ARRBLCKCROSS));
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::TrackKey(const QPoint& p)
{
int nKey = m_nHitKeyIndex;
QPoint point = p;
if (nKey >= 0)
{
float time, val;
// Editing time & value.
Limit(point.rx(), m_rcSpline.left(), m_rcSpline.right());
Limit(point.ry(), m_rcSpline.top(), m_rcSpline.bottom());
PointToTimeValue(point, time, val);
int i;
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
{
// Switch to next key.
if (fabs(m_pSpline->GetKeyTime(i) - time) < MIN_TIME_EPSILON)
{
if (i != nKey)
{
return;
}
}
}
m_pSpline->SetKeyValueFloat(nKey, val);
if ((nKey != 0 && nKey != m_pSpline->GetKeyCount() - 1) || !m_bLockFirstLastKey)
{
m_pSpline->SetKeyTime(nKey, time);
}
else if (m_bLockFirstLastKey)
{
int first = 0;
int last = m_pSpline->GetKeyCount() - 1;
if (nKey == first)
{
m_pSpline->SetKeyValueFloat(last, val);
}
else if (nKey == last)
{
m_pSpline->SetKeyValueFloat(first, val);
}
}
m_pSpline->Update();
emit change();
if (m_updateCallback)
{
m_updateCallback(this);
}
update();
}
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::StopTracking()
{
if (!m_bTracking)
{
return;
}
GetIEditor()->AcceptUndo("Spline Move");
m_bTracking = FALSE;
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::RemoveKey(int nKey)
{
if (!m_pSpline)
{
return;
}
if (nKey)
{
if (m_bLockFirstLastKey)
{
if (nKey == 0 || nKey == m_pSpline->GetKeyCount() - 1)
{
return;
}
}
}
CUndo undo("Remove Spline Key");
emit beforeChange();
m_nActiveKey = -1;
m_nHitKeyIndex = -1;
if (m_pSpline)
{
m_pSpline->RemoveKey(nKey);
m_pSpline->Update();
ValidateSpline();
}
emit change();
if (m_updateCallback)
{
m_updateCallback(this);
}
update();
}
//////////////////////////////////////////////////////////////////////////
int CSplineCtrl::InsertKey(const QPoint& point)
{
CUndo undo("Spline Insert Key");
float time, val;
PointToTimeValue(point, time, val);
int i;
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
{
// Skip if any key already have time that is very close.
if (fabs(m_pSpline->GetKeyTime(i) - time) < MIN_TIME_EPSILON)
{
return i;
}
}
emit beforeChange();
m_pSpline->InsertKeyFloat(time, val);
m_pSpline->Update();
ClearSelection();
update();
emit change();
if (m_updateCallback)
{
m_updateCallback(this);
}
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
{
// Find key with added time.
if (m_pSpline->GetKeyTime(i) == time)
{
return i;
}
}
return -1;
}
void CSplineCtrl::ToggleKeySlope(int nIndex, int nDir)
{
if (nIndex >= 0)
{
int flags = m_pSpline->GetKeyFlags(nIndex);
if (nDir <= 0)
{
// Toggle left side.
flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_IN_SHIFT;
}
if (nDir >= 0)
{
// Toggle right side.
flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_OUT_SHIFT;
}
m_pSpline->SetKeyFlags(nIndex, flags);
m_pSpline->Update();
emit change();
if (m_updateCallback)
{
m_updateCallback(this);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::ClearSelection()
{
m_nActiveKey = -1;
if (m_pSpline)
{
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
}
for (int i = 0; i < (int)m_bSelectedKeys.size(); i++)
{
m_bSelectedKeys[i] = false;
}
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::SetTimeMarker(float fTime)
{
if (!m_pSpline)
{
return;
}
if (fTime == m_fTimeMarker)
{
return;
}
// Erase old first.
QPoint pt0 = TimeToPoint(m_fTimeMarker);
QPoint pt1 = TimeToPoint(fTime);
QRect rc(QPoint(pt0.x(), m_rcSpline.top()), QPoint(pt1.x(), m_rcSpline.bottom()));
rc = rc.normalized();
rc.adjust(-5, 0, 5, 0);
rc = rc.intersected(m_rcSpline);
m_TimeUpdateRect = rc;
update(rc);
m_fTimeMarker = fTime;
}
//////////////////////////////////////////////////////////////////////////
void CSplineCtrl::SetTimelineCtrl(TimelineWidget* pTimelineCtrl)
{
m_pTimelineCtrl = pTimelineCtrl;
if (m_pTimelineCtrl)
{
m_pTimelineCtrl->setParent(this);
}
}
#include <Controls/moc_SplineCtrl.cpp>
+160
View File
@@ -0,0 +1,160 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_SPLINECTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_SPLINECTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <ISplines.h>
#endif
// Custom styles for this control.
#define SPLINE_STYLE_NOGRID 0x0001
#define SPLINE_STYLE_NO_TIME_MARKER 0x0002
// Notify event sent when spline is being modified.
#define SPLN_CHANGE (0x0001)
// Notify event sent just before when spline is modified.
#define SPLN_BEFORE_CHANGE (0x0002)
class TimelineWidget;
//////////////////////////////////////////////////////////////////////////
// Spline control.
//////////////////////////////////////////////////////////////////////////
class CSplineCtrl
: public QWidget
{
Q_OBJECT
public:
CSplineCtrl(QWidget* parent = nullptr);
virtual ~CSplineCtrl();
//Key functions
int GetActiveKey() { return m_nActiveKey; };
void SetActiveKey(int nIndex);
int InsertKey(const QPoint& point);
void ToggleKeySlope(int nIndex, int nDist);
void SetGrid(int numX, int numY) { m_gridX = numX; m_gridY = numY; };
void SetTimeRange(float tmin, float tmax) { m_fMinTime = tmin; m_fMaxTime = tmax; }
void SetValueRange(float tmin, float tmax)
{
m_fMinValue = tmin;
m_fMaxValue = tmax;
if (m_fMinValue == m_fMaxValue)
{
m_fMaxValue = m_fMinValue + 0.001f;
}
}
void SetTooltipValueScale(float x, float y) { m_fTooltipScaleX = x; m_fTooltipScaleY = y; };
// Lock value of first and last key to be the same.
void LockFirstAndLastKeys(bool bLock) { m_bLockFirstLastKey = bLock; }
void SetSpline(ISplineInterpolator* pSpline, BOOL bRedraw = FALSE);
ISplineInterpolator* GetSpline();
void SetTimeMarker(float fTime);
void SetTimelineCtrl(TimelineWidget* pTimelineCtrl);
void UpdateToolTip();
typedef Functor1<CSplineCtrl*> UpdateCallback;
void SetUpdateCallback(UpdateCallback cb) { m_updateCallback = cb; };
Q_SIGNALS:
void beforeChange();
void change();
protected:
enum EHitCode
{
HIT_NOTHING,
HIT_KEY,
HIT_SPLINE,
};
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void paintEvent(QPaintEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
void OnLButtonDown(const QPoint& point, Qt::KeyboardModifiers modifiers);
void mouseMoveEvent(QMouseEvent* event) override;
void OnLButtonUp(const QPoint& point, Qt::KeyboardModifiers modifiers);
void OnRButtonDown(const QPoint& point, Qt::KeyboardModifiers modifiers);
void OnSetCursor();
void mouseDoubleClickEvent(QMouseEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
// Drawing functions
void DrawGrid(QPainter* pDC);
void DrawSpline(QPainter* pDC);
void DrawKeys(QPainter* pDC);
void DrawTimeMarker(QPainter* pDC);
EHitCode HitTest(const QPoint& point);
//Tracking support helper functions
void StartTracking();
void TrackKey(const QPoint& point);
void StopTracking();
void RemoveKey(int nKey);
QPoint KeyToPoint(int nKey);
QPoint TimeToPoint(float time);
void PointToTimeValue(const QPoint& point, float& time, float& value);
float XOfsToTime(int x);
QPoint XOfsToPoint(int x);
void ClearSelection();
void ValidateSpline();
private:
ISplineInterpolator* m_pSpline;
QRect m_rcClipRect;
QRect m_rcSpline;
QPoint m_hitPoint;
EHitCode m_hitCode;
int m_nHitKeyIndex;
int m_nHitKeyDist;
float m_fTimeMarker;
int m_nActiveKey;
int m_nKeyDrawRadius;
bool m_bTracking;
int m_gridX;
int m_gridY;
float m_fMinTime, m_fMaxTime;
float m_fMinValue, m_fMaxValue;
float m_fTooltipScaleX, m_fTooltipScaleY;
bool m_bLockFirstLastKey;
std::vector<int> m_bSelectedKeys;
TimelineWidget* m_pTimelineCtrl;
QRect m_TimeUpdateRect;
UpdateCallback m_updateCallback;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_SPLINECTRL_H
File diff suppressed because it is too large Load Diff
+420
View File
@@ -0,0 +1,420 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_SPLINECTRLEX_H
#define CRYINCLUDE_EDITOR_CONTROLS_SPLINECTRLEX_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <ISplines.h>
#include "Controls/WndGridHelper.h"
#include "IKeyTimeSet.h"
#include "Undo/IUndoObject.h"
#include <QWidget>
#endif
// Custom styles for this control.
#define SPLINE_STYLE_NOGRID 0x0001
#define SPLINE_STYLE_NO_TIME_MARKER 0x0002
// Notify event sent when spline is being modified.
#define SPLN_CHANGE (0x0001)
// Notify event sent just before when spline is modified.
#define SPLN_BEFORE_CHANGE (0x0002)
// Notify when spline control is scrolled/zoomed.
#define SPLN_SCROLL_ZOOM (0x0003)
// Notify when time changed.
#define SPLN_TIME_START_CHANGE (0x0001)
#define SPLN_TIME_END_CHANGE (0x0002)
#define SPLN_TIME_CHANGE (0x0004)
// Notify event sent when a key selection changes
#define SPLN_KEY_SELECTION_CHANGE (0x0005)
#ifndef NM_CLICK
#define NM_CLICK (-2)
#define NM_RCLICK (-5)
#endif
#ifdef LoadCursor
#undef LoadCursor
#endif
class AbstractTimelineWidget;
class TimelineWidget;
class QRubberBand;
class ISplineSet
{
public:
virtual ISplineInterpolator* GetSplineFromID(const string& id) = 0;
virtual string GetIDFromSpline(ISplineInterpolator* pSpline) = 0;
virtual int GetSplineCount() const = 0;
virtual int GetKeyCountAtTime(float time, float threshold) const = 0;
};
class ISplineCtrlUndo
: public IUndoObject
{
public:
virtual bool IsSelectionChanged() const = 0;
};
class AbstractSplineWidget
: public IKeyTimeSet
{
friend class CUndoSplineCtrlEx;
public:
AbstractSplineWidget();
virtual ~AbstractSplineWidget();
int InsertKey(ISplineInterpolator* pSpline, ISplineInterpolator* pDetailSpline, const QPoint& point);
virtual void update() = 0;
virtual void update(const QRect& rect) = 0;
virtual QPoint mapFromGlobal(const QPoint& point) const = 0;
virtual void SetCapture() {}
virtual QWidget* WidgetCast() = 0;
void SetGrid(int numX, int numY) { m_gridX = numX; m_gridY = numY; };
void SetTimeRange(const Range& range) { m_timeRange = range; }
void SetValueRange(const Range& range) { m_valueRange = range; }
void SetDefaultValueRange(const Range& range) { m_defaultValueRange = range; }
void SetDefaultKeyTangentType(ESplineKeyTangentType type) { m_defaultKeyTangentType = type; }
ESplineKeyTangentType GetDefaultKeyTangentType() const { return m_defaultKeyTangentType; }
void SetTooltipValueScale(float x, float y) { m_fTooltipScaleX = x; m_fTooltipScaleY = y; };
void SetSplineSet(ISplineSet* pSplineSet);
void AddSpline(ISplineInterpolator* pSpline, ISplineInterpolator* pDetailSpline, const QColor& color);
void AddSpline(ISplineInterpolator * pSpline, ISplineInterpolator * pDetailSpline, QColor anColorArray[4]);
void RemoveSpline(ISplineInterpolator* pSpline);
void RemoveAllSplines();
int GetSplineCount() const { return m_splines.size(); }
ISplineInterpolator* GetSpline(int nIndex) const { return m_splines[nIndex].pSpline; }
void SetTimeMarker(float fTime);
float GetTimeMarker() const { return m_fTimeMarker; }
void SetTimeScale(float timeScale) { m_fTimeScale = timeScale; }
void SetGridTimeScale(float fGridTimeScale) { m_fGridTimeScale = fGridTimeScale; }
float GetGridTimeScale() { return m_fGridTimeScale; }
void SetMinTimeEpsilon(float fMinTimeEpsilon) { m_fMinTimeEpsilon = fMinTimeEpsilon; }
float GetMinTimeEpsilon() const { return m_fMinTimeEpsilon; }
void SetSnapTime(bool bOn) { m_bSnapTime = bOn; }
void SetSnapValue(bool bOn) { m_bSnapValue = bOn; }
bool IsSnapTime() const { return m_bSnapTime; }
bool IsSnapValue() const { return m_bSnapValue; }
float SnapTimeToGridVertical(float time);
void OnUserCommand(UINT cmd);
void FitSplineToViewWidth();
void FitSplineToViewHeight();
void FitSplineHeightToValueRange();
void CopyKeys();
void PasteKeys();
void StoreUndo();
void ZeroAll();
void KeyAll();
void SelectAll();
void RemoveSelectedKeyTimes();
void RedrawWindowAroundMarker();
void SplinesChanged();
void SetControlAmplitude(bool controlAmplitude);
bool GetControlAmplitude() const;
//void SelectPreviousKey();
//void SelectNextKey();
void GotoNextKey(bool previousKey);
void RemoveAllKeysButThis();
//////////////////////////////////////////////////////////////////////////
// Scrolling/Zooming.
//////////////////////////////////////////////////////////////////////////
Vec2 ClientToWorld(const QPoint& point);
QPoint WorldToClient(Vec2 v);
Vec2 GetZoom();
void SetZoom(Vec2 zoom, const QPoint& center);
void SetZoom(Vec2 zoom);
void SetScrollOffset(Vec2 ofs);
Vec2 GetScrollOffset();
float SnapTime(float time);
float SnapValue(float val);
//////////////////////////////////////////////////////////////////////////
// IKeyTimeSet Implementation
virtual int GetKeyTimeCount() const;
virtual float GetKeyTime(int index) const;
virtual void MoveKeyTimes(int numChanges, int* indices, float scale, float offset, bool copyKeys);
virtual bool GetKeyTimeSelected(int index) const;
virtual void SetKeyTimeSelected(int index, bool selected);
virtual int GetKeyCount(int index) const;
virtual int GetKeyCountBound() const;
virtual void BeginEdittingKeyTimes();
virtual void EndEdittingKeyTimes();
void SetEditLock(bool bLock) { m_bEditLock = bLock; }
int leftBorderOffset() const
{
return m_nLeftOffset;
}
protected:
enum EHitCode
{
HIT_NOTHING,
HIT_KEY,
HIT_SPLINE,
HIT_TIMEMARKER,
HIT_TANGENT_HANDLE
};
enum EEditMode
{
NothingMode = 0,
SelectMode,
TrackingMode,
ScrollZoomMode,
ScrollMode,
ZoomMode,
TimeMarkerMode,
};
struct SSplineInfo
{
QColor anColorArray[4];
ISplineInterpolator* pSpline;
ISplineInterpolator* pDetailSpline;
};
virtual bool GetTangentHandlePts(QPoint& inTangentPt, QPoint& pt, QPoint& outTangentPt, int nSpline, int nKey, int nDimension);
EHitCode HitTest(const QPoint& point);
ISplineInterpolator* HitSpline(const QPoint& point);
//Tracking support helper functions
void StartTracking(bool copyKeys);
void StopTracking();
void RemoveKey(ISplineInterpolator* pSpline, int nKey);
void RemoveSelectedKeys();
void RemoveSelectedKeyTimesImpl();
void MoveSelectedKeys(Vec2 offset, bool copyKeys);
void ScaleAmplitudeKeys(float time, float startValue, float offset);
void TimeScaleKeys(float time, float startTime, float endTime);
void ValueScaleKeys(float startValue, float endValue);
void ModifySelectedKeysFlags(int nRemoveFlags, int nAddFlags);
QPoint TimeToPoint(float time, ISplineInterpolator* pSpline);
float TimeToXOfs(float x);
void PointToTimeValue(QPoint point, float& time, float& value);
float XOfsToTime(int x);
QPoint XOfsToPoint(int x, ISplineInterpolator* pSpline);
virtual void ClearSelection();
virtual void SelectKey(ISplineInterpolator* pSpline, int nKey, int nDimension, bool bSelect);
bool IsKeySelected(ISplineInterpolator* pSpline, int nKey, int nDimension) const;
int GetNumSelected();
void SetHorizontalExtent(int min, int max);
virtual void SendNotifyEvent(int nEvent) = 0;
virtual void SelectRectangle(const QRect& rc, bool bSelect);
//////////////////////////////////////////////////////////////////////////
void UpdateKeyTimes() const;
void ConditionalStoreUndo();
void ClearSelectedKeys();
void DuplicateSelectedKeys();
Range GetSplinesRange();
virtual void captureMouseImpl() = 0;
virtual void releaseMouseImpl() = 0;
virtual void setCursorImpl(UINT cursor) = 0;
virtual ISplineCtrlUndo* CreateSplineCtrlUndoObject(std::vector<ISplineInterpolator*>& splineContainer);
QRect m_rcClipRect;
QRect m_rcSpline;
QRect m_rcClient;
QPoint m_cMousePos;
QPoint m_cMouseDownPos;
QPoint m_hitPoint;
EHitCode m_hitCode;
int m_nHitKeyIndex;
int m_nHitDimension;
int m_bHitIncomingHandle;
ISplineInterpolator* m_pHitSpline;
ISplineInterpolator* m_pHitDetailSpline;
QPoint m_curvePoint;
float m_fTimeMarker;
int m_nKeyDrawRadius;
bool m_bSnapTime;
bool m_bSnapValue;
bool m_bBitmapValid;
int m_gridX;
int m_gridY;
float m_fMinTime, m_fMaxTime;
float m_fMinValue, m_fMaxValue;
float m_fTooltipScaleX, m_fTooltipScaleY;
float m_fMinTimeEpsilon;
QPoint m_lastToolTipPos;
QString m_tooltipText;
QRect m_rcSelect;
QRect m_TimeUpdateRect;
float m_fTimeScale;
float m_fValueScale;
float m_fGridTimeScale;
Range m_timeRange;
Range m_valueRange;
Range m_defaultValueRange;
//! This is how often to place ticks.
//! value of 10 means place ticks every 10 second.
double m_ticksStep;
EEditMode m_editMode;
int m_nLeftOffset;
CWndGridHelper m_grid;
//////////////////////////////////////////////////////////////////////////
std::vector<SSplineInfo> m_splines;
mutable bool m_bKeyTimesDirty;
class KeyTime
{
public:
KeyTime(float time, int count)
: time(time)
, oldTime(0.0f)
, selected(false)
, count(count) {}
bool operator<(const KeyTime& other) const { return this->time < other.time; }
float time;
float oldTime;
bool selected;
int count;
};
mutable std::vector<KeyTime> m_keyTimes;
mutable int m_totalSplineCount;
static const float threshold;
bool m_copyKeys;
bool m_startedDragging;
bool m_controlAmplitude;
ESplineKeyTangentType m_defaultKeyTangentType;
// Improving mouse control...
bool m_boLeftMouseButtonDown;
ISplineSet* m_pSplineSet;
bool m_bEditLock;
ISplineCtrlUndo* m_pCurrentUndo;
AbstractTimelineWidget* m_pTimelineCtrl;
};
//////////////////////////////////////////////////////////////////////////
// Spline control.
//////////////////////////////////////////////////////////////////////////
class SplineWidget
: public QWidget
, public AbstractSplineWidget
{
Q_OBJECT
public:
SplineWidget(QWidget* parent);
virtual ~SplineWidget();
void update() { QWidget::update(); }
void update(const QRect& rect) { QWidget::update(rect); }
QPoint mapFromGlobal(const QPoint& point) const override { return QWidget::mapFromGlobal(point); }
void SetTimelineCtrl(TimelineWidget* pTimelineCtrl);
QWidget* WidgetCast() override { return this; }
Q_SIGNALS:
void beforeChange();
void change();
void timeChange();
void scrollZoomRequested();
void clicked();
void rightClicked();
void keySelectionChange();
protected:
void paintEvent(QPaintEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
bool event(QEvent* event) override;
void OnLButtonDown(const QPoint& point, Qt::KeyboardModifiers modifiers);
void OnLButtonUp(const QPoint& point, Qt::KeyboardModifiers modifiers);
void OnRButtonDown(const QPoint& point, Qt::KeyboardModifiers modifiers);
void OnMButtonDown(const QPoint& point, Qt::KeyboardModifiers modifiers);
void OnMButtonUp(const QPoint& point, Qt::KeyboardModifiers modifiers);
void DrawGrid(QPainter* painter);
void DrawSpline(QPainter* pDC, SSplineInfo& splineInfo, float startTime, float endTime);
void DrawKeys(QPainter* painter, int splineIndex, float startTime, float endTime);
void DrawTimeMarker(QPainter* pDC);
void DrawTangentHandle(QPainter* pDC, int nSpline, int nKey, int nDimension);
void SendNotifyEvent(int nEvent) override;
void captureMouseImpl() override { grabMouse(); }
void releaseMouseImpl() override { releaseMouse(); }
void setCursorImpl(UINT cursor) override { setCursor(CMFCUtils::LoadCursor(cursor)); }
protected:
QRubberBand* m_rubberBand;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_SPLINECTRLEX_H
@@ -0,0 +1,461 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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.
*
*/
// SyntaxColorizer.cpp: implementation of the CSyntaxColorizer class.
//
// Version: 1.0.0
// Author: Jeff Schering jeffschering@hotmail.com
// Date: Jan 2001
// Copyright 2001 by Jeff Schering
//
//////////////////////////////////////////////////////////////////////
#include "EditorDefs.h"
#include "SyntaxColorizer.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSyntaxColorizer::CSyntaxColorizer(QTextDocument *pParent)
: QSyntaxHighlighter(pParent)
{
createDefaultCharFormat();
SetCommentColor(CLR_COMMENT);
SetStringColor(CLR_STRING);
createTables();
m_pskKeyword = NULL;
createDefaultKeywordList();
}
CSyntaxColorizer::~CSyntaxColorizer()
{
ClearKeywordList();
deleteTables();
}
//////////////////////////////////////////////////////////////////////
// Member Functions
//////////////////////////////////////////////////////////////////////
void CSyntaxColorizer::createDefaultCharFormat()
{
m_cfComment = m_cfDefault;
m_cfString = m_cfDefault;
}
void CSyntaxColorizer::createDefaultKeywordList()
{
const char *sKeywords = "__asm,else,main,struct,__assume,enum,"
"__multiple_inheritance,switch,auto,__except,__single_inheritance,"
"template,__based,explicit,__virtual_inheritance,this,bool,extern,"
"mutable,thread,break,false,naked,throw,case,__fastcall,namespace,"
"true,catch,__finally,new,try,__cdecl,float,noreturn,__try,char,for,"
"operator,typedef,class,friend,private,typeid,const,goto,protected,"
"typename,const_cast,if,public,union,continue,inline,register,"
"unsigned,__declspec,__inline,reinterpret_cast,using,declaration,"
"directive,default,int,return,uuid,delete,__int8,short,"
"__uuidof,dllexport,__int16,signed,virtual,dllimport,__int32,sizeof,"
"void,do,__int64,static,volatile,double,__leave,static_cast,wmain,"
"dynamic_cast,long,__stdcall,while";
const char *sDirectives = "#define,#elif,#else,#endif,#error,#ifdef,"
"#ifndef,#import,#include,#line,#pragma,#undef";
const char *sPragmas = "alloc_text,comment,init_seg1,optimize,auto_inline,"
"component,inline_depth,pack,bss_seg,data_seg,"
"inline_recursion,pointers_to_members1,check_stack,"
"function,intrinsic,setlocale,code_seg,hdrstop,message,"
"vtordisp1,const_seg,include_alias,once,warning";
AddKeyword(sKeywords,CLR_KEYWORD,GRP_KEYWORD);
AddKeyword(sDirectives,CLR_KEYWORD,GRP_KEYWORD);
AddKeyword(sPragmas,CLR_KEYWORD,GRP_KEYWORD);
}
void CSyntaxColorizer::createTables()
{
m_pTableZero = new unsigned char[256]; m_pTableOne = new unsigned char[256];
m_pTableTwo = new unsigned char[256]; m_pTableThree = new unsigned char[256];
m_pTableFour = new unsigned char[256]; m_pAllowable = new unsigned char[256];
memset(m_pTableZero,SKIP,256); memset(m_pTableOne,SKIP,256);
memset(m_pTableTwo,SKIP,256); memset(m_pTableThree,SKIP,256);
memset(m_pTableFour,SKIP,256); memset(m_pAllowable,false,256);
*(m_pTableZero + int('"')) = DQSTART; *(m_pTableZero + int('\'')) = SQSTART;
*(m_pTableZero + int('/')) = CMSTART; *(m_pTableOne + int('"')) = DQEND;
*(m_pTableTwo + int('\'')) = SQEND;
*(m_pTableFour + int('*')) = MLEND;
*(m_pAllowable + int('\n')) = true; *(m_pAllowable + int('\r')) = true;
*(m_pAllowable + int('\t')) = true; *(m_pAllowable + int('\0')) = true;
*(m_pAllowable + int(' ')) = true; *(m_pAllowable + int(';')) = true;
*(m_pAllowable + int('(')) = true; *(m_pAllowable + int(')')) = true;
*(m_pAllowable + int('{')) = true; *(m_pAllowable + int('}')) = true;
*(m_pAllowable + int('[')) = true; *(m_pAllowable + int(']')) = true;
*(m_pAllowable + int('*')) = true;
}
void CSyntaxColorizer::deleteTables()
{
delete m_pTableZero; delete m_pTableOne; delete m_pTableTwo;
delete m_pTableThree; delete m_pTableFour; delete m_pAllowable;
}
void CSyntaxColorizer::AddKeyword(const char *Keyword, const QBrush &cr, int grp)
{
QTextCharFormat cf = m_cfDefault;
cf.setForeground(cr);
for (auto token : QString(Keyword).split(","))
{
addKey(token,cf,grp);
}
}
void CSyntaxColorizer::AddKeyword(const char *Keyword, const QTextCharFormat& cf, int grp)
{
for (auto token : QString(Keyword).split(","))
{
addKey(token,cf,grp);
}
}
void CSyntaxColorizer::addKey(const QString& Keyword, const QTextCharFormat& cf, int grp) //add in ascending order
{
SKeyword* pskNewKey = new SKeyword;
SKeyword* prev,*curr;
pskNewKey->keyword = Keyword;
m_keywords[pskNewKey->keyword] = pskNewKey;
pskNewKey->cf = cf;
pskNewKey->group = grp;
pskNewKey->pNext = NULL;
*(m_pTableZero + pskNewKey->keyword[0].toLatin1()) = KEYWORD;
//if list is empty, add first node
if(m_pskKeyword == NULL)
m_pskKeyword = pskNewKey;
else
{
//check to see if new node goes before first node
if(Keyword.compare(m_pskKeyword->keyword) < 0)
{
pskNewKey->pNext = m_pskKeyword;
m_pskKeyword = pskNewKey;
}
//check to see if new keyword already exists at the first node
else if(Keyword == m_pskKeyword->keyword)
{
//the keyword exists, so replace the existing with the new
pskNewKey->pNext = m_pskKeyword->pNext;
delete m_pskKeyword;
m_pskKeyword = pskNewKey;
}
else
{
prev = m_pskKeyword;
curr = m_pskKeyword->pNext;
while(curr != NULL && curr->keyword.compare(Keyword) < 0)
{
prev = curr;
curr = curr->pNext;
}
if(curr != NULL && curr->keyword == Keyword)
{
//the keyword exists, so replace the existing with the new
prev->pNext = pskNewKey;
pskNewKey->pNext = curr->pNext;
delete curr;
}
else
{
pskNewKey->pNext = curr;
prev->pNext = pskNewKey;
}
}
}
}
void CSyntaxColorizer::ClearKeywordList()
{
SKeyword* pTemp = m_pskKeyword;
m_keywords.clear();
while(m_pskKeyword != NULL)
{
*(m_pTableZero + m_pskKeyword->keyword[0].toLatin1()) = SKIP;
pTemp = m_pskKeyword->pNext;
delete m_pskKeyword;
m_pskKeyword = pTemp;
}
}
QString CSyntaxColorizer::GetKeywordList()
{
QString sList;
SKeyword* pTemp = m_pskKeyword;
while(pTemp != NULL)
{
if(!sList.isEmpty())
sList += QLatin1Char(',');
sList += pTemp->keyword;
pTemp = pTemp->pNext;
}
return sList;
}
QString CSyntaxColorizer::GetKeywordList(int grp)
{
QString sList;
SKeyword* pTemp = m_pskKeyword;
while(pTemp != NULL)
{
if(pTemp->group == grp)
{
if(!sList.isEmpty())
sList += QLatin1Char(',');
sList += pTemp->keyword;
}
pTemp = pTemp->pNext;
}
return sList;
}
void CSyntaxColorizer::SetCommentColor(const QBrush& cr)
{
QTextCharFormat cf = m_cfComment;
cf.setForeground(cr);
SetCommentStyle(cf);
}
void CSyntaxColorizer::SetStringColor(const QBrush& cr)
{
QTextCharFormat cf = m_cfString;
cf.setForeground(cr);
SetStringStyle(cf);
}
void CSyntaxColorizer::SetGroupStyle(int grp, const QTextCharFormat& cf)
{
SKeyword* pTemp = m_pskKeyword;
while(pTemp != NULL)
{
if(pTemp->group == grp)
{
pTemp->cf = cf;
}
pTemp = pTemp->pNext;
}
}
void CSyntaxColorizer::GetGroupStyle(int grp, QTextCharFormat& cf)
{
SKeyword* pTemp = m_pskKeyword;
while(pTemp != NULL)
{
if(pTemp->group == grp)
{
cf = pTemp->cf;
pTemp = NULL;
}
else
{
pTemp = pTemp->pNext;
//if grp is not found, return default style
if(pTemp == NULL) cf = m_cfDefault;
}
}
}
void CSyntaxColorizer::SetGroupColor(int grp, const QBrush& cr)
{
QTextCharFormat cf;
GetGroupStyle(grp,cf);
cf.setForeground(cr);
SetGroupStyle(grp,cf);
}
void CSyntaxColorizer::highlightBlock(const QString& text)
{
//setup some vars
char sWord[4096];
QTextCharFormat cf;
const char *lpszTemp;
long iStart = 0;
long x = 0;
SKeyword* pskTemp = m_pskKeyword;
QByteArray latin1Text = text.toLatin1();
const char *lpszBuf = latin1Text.constData();
const unsigned char *ppTables[] = { m_pTableZero, m_pTableOne, m_pTableTwo, m_pTableThree, m_pTableFour };
int iState = previousBlockState();
if (iState == -1)
iState = 0;
//do the work
while(lpszBuf[x])
{
switch(ppTables[iState][int(lpszBuf[x])])
{
case DQSTART:
iState = 1;
iStart = x;
break;
case SQSTART:
iState = 2;
iStart = x;
break;
case CMSTART:
if(lpszBuf[x+1] == '/')
{
iState = 3;
iStart = x;
x++;
}
else if(lpszBuf[x+1] == '*')
{
iState = 4;
iStart = x;
x++;
}
else if(lpszBuf[x] == '\'')
{
iState = 3;
iStart = x;
x++;
}
break;
case MLEND:
if(lpszBuf[x+1] == '/')
{
x++;
iState = 0;
setFormat(iStart, x + 1 - iStart, m_cfComment);
}
break;
case DQEND:
iState = 0;
setFormat(iStart, x + 1 - iStart, m_cfString);
break;
case SQEND:
if(lpszBuf[x-1] == '\\' && lpszBuf[x+1] == '\'')
break;
iState = 0;
setFormat(iStart, x + 1 - iStart, m_cfString);
break;
case KEYWORD:
// Extract whole word.
lpszTemp = lpszBuf+x;
{
if (x > 0 && !m_pAllowable[int(lpszBuf[x-1])])
break;
int i = 0;
while (!m_pAllowable[int(lpszTemp[i])])
{
sWord[i] = lpszTemp[i];
i++;
}
if (i > 0)
{
sWord[i] = 0;
// Word extracted.
// Find keyword.
Keywords::iterator it = m_keywords.find( QString(sWord) );
if (it != m_keywords.end())
{
int iStart2 = x;
pskTemp = it->second;
x += pskTemp->keyword.size();
setFormat(iStart2, x - iStart2, pskTemp->cf);
}
}
}
/*
lpszTemp = lpszBuf+x;
while(pskTemp != NULL)
{
if(pskTemp->keyword[0] == lpszTemp[0])
{
int x1=0,y1=0;iStart = iOffset + x;
while(pskTemp->keyword[x1])
{
y1 += lpszTemp[x1] ^ pskTemp->keyword[x1];
x1++;
}
if(y1 == 0 && (*(m_pAllowable + (lpszBuf[x-1])) &&
*(m_pAllowable + (lpszBuf[x+pskTemp->keylen]))))
{
if(_stricmp(pskTemp->keyword,"rem") == 0)
{
pTable = m_pTableThree;
}
else
{
x += pskTemp->keylen;
pCtrl->SetSel(iStart,iOffset + x);
pCtrl->SetSelectionCharFormat(pskTemp->cf);
}
}
}
pskTemp = pskTemp->pNext;
}
*/
pskTemp = m_pskKeyword;
break;
case SKIP:;
}
x++;
}
//sometimes we get to the end of the file before the end of the string
//or comment, so we deal with that situation here
if(iState == 1)
{
setFormat(iStart, x + 1 - iStart, m_cfString);
}
else if(iState == 2)
{
setFormat(iStart, x + 1 - iStart, m_cfString);
}
else if(iState == 3)
{
setFormat(iStart, x + 1 - iStart, m_cfComment);
if(lpszBuf[x-2] != '\\') // line continuation character
iState = 0;
}
else if(iState == 4)
{
setFormat(iStart, x + 1 - iStart, m_cfComment);
}
setCurrentBlockState(iState);
}
#include <Controls/moc_SyntaxColorizer.cpp>
@@ -0,0 +1,114 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : interface for the CSyntaxColorizer class.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_SYNTAXCOLORIZER_H
#define CRYINCLUDE_EDITOR_CONTROLS_SYNTAXCOLORIZER_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QSyntaxHighlighter>
#endif
#define CLR_STRING QColor(55, 0, 200)
#define CLR_PLAIN QColor(0, 0, 0)
#define CLR_COMMENT QColor(0, 170, 0)
#define CLR_KEYWORD QColor(0, 0, 255)
#define GRP_KEYWORD 0
class CSyntaxColorizer
: public QSyntaxHighlighter
{
Q_OBJECT
public:
CSyntaxColorizer(QTextDocument* pParent = nullptr);
virtual ~CSyntaxColorizer();
//protected vars
protected:
unsigned char* m_pTableZero, * m_pTableOne;
unsigned char* m_pTableTwo, * m_pTableThree;
unsigned char* m_pTableFour, * m_pAllowable;
enum Types
{
SKIP,
DQSTART, //Double Quotes start
DQEND, //Double Quotes end
SQSTART, //Single Quotes start
SQEND, //Single Quotes end
CMSTART, //Comment start (both single and multi line)
MLEND, //Multi line comment end
KEYWORD //Keyword start
} m_type;
struct SKeyword
{
QString keyword;
int keylen;
QTextCharFormat cf;
int group;
SKeyword* pNext;
SKeyword() { pNext = NULL; }
};
SKeyword* m_pskKeyword;
QTextCharFormat m_cfComment;
QTextCharFormat m_cfString;
QTextCharFormat m_cfDefault;
//typedef std::map<LPCSTR,SKeyword*,> Keywords;
//typedef std::hash_map<const char*,SKeyword*,stl::hash_strcmp<const char*> > Keywords;
//typedef std::map<const char*,SKeyword*,stl::hash_strcmp<const char*> > Keywords;
typedef std::map<QString, SKeyword*> Keywords;
Keywords m_keywords;
//protected member functions
protected:
void addKey(const QString& Keyword, const QTextCharFormat& cf, int grp);
void createTables();
void deleteTables();
void createDefaultKeywordList();
void createDefaultCharFormat();
//public member functions
public:
void highlightBlock(const QString& text) override;
void GetCommentStyle(QTextCharFormat& cf) { cf = m_cfComment; };
void GetStringStyle(QTextCharFormat& cf) { cf = m_cfString; };
void GetGroupStyle(int grp, QTextCharFormat& cf);
void GetDefaultStyle(QTextCharFormat& cf) { cf = m_cfDefault; };
void SetCommentStyle(const QTextCharFormat& cf) { m_cfComment = cf; };
void SetCommentColor(const QBrush& cr);
void SetStringStyle(const QTextCharFormat& cf) { m_cfString = cf; };
void SetStringColor(const QBrush& cr);
void SetGroupStyle(int grp, const QTextCharFormat& cf);
void SetGroupColor(int grp, const QBrush& cr);
void SetDefaultStyle(const QTextCharFormat& cf) { m_cfDefault = cf; };
void AddKeyword(const char* Keyword, const QTextCharFormat& cf, int grp = 0);
void AddKeyword(const char* Keyword, const QBrush& cr, int grp = 0);
void ClearKeywordList();
QString GetKeywordList();
QString GetKeywordList(int grp);
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_SYNTAXCOLORIZER_H
@@ -0,0 +1,154 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "TextEditorCtrl.h"
#define GRP_KEYWORD 0
#define GRP_CONSTANTS 1
#define GRP_DIRECTIVE 2
#define GRP_PRAGMA 3
// CTextEditorCtrl
CTextEditorCtrl::CTextEditorCtrl(QWidget* pParent)
: QTextEdit(pParent)
, m_sc(document())
{
/*
//reconfigure CSyntaxColorizer's default keyword groupings
LPTSTR sKeywords = "for,for,else,main,struct,enum,switch,auto,"
"template,explicit,this,bool,extern,thread,break,false,"
"throw,case,namespace,true,catch,new,try,float,noreturn,"
"char,operator,typedef,class,friend,private,const,goto,"
"protected,typename,if,public,union,continue,inline,"
"unsigned,using,directive,default,int,return,delete,short,"
"signed,virtual,sizeof,void,do,static,double,long,while";
LPTSTR sDirectives = "#define,#elif,#else,#endif,#error,#ifdef,"
"#ifndef,#import,#include,#line,#pragma,#undef";
LPTSTR sPragmas = "comment,optimize,auto_inline,once,warning,"
"component,pack,function,intrinsic,setlocale,hdrstop,message";
m_sc.ClearKeywordList();
m_sc.AddKeyword(sKeywords,QColor(0,0,255),GRP_KEYWORD);
m_sc.AddKeyword(sDirectives,QColor(0,0,255),GRP_DIRECTIVE);
m_sc.AddKeyword(sPragmas,QColor(0,0,255),GRP_PRAGMA);
m_sc.AddKeyword("REM,Rem,rem",QColor(255,0,255),4);
*/
const char* sKeywords = "Shader,ShadeLayer,HW,LightStyle,ValueString,Orient,Origin,Params,Array,Template,Templates,"
"Version,CGVProgram,CGVPParam,Name,"
"DeclareLightMaterial,Side,Ambient,Diffuse,Specular,Emission,Shininess,"
"Layer,Map,RGBGen,RgbGen,AlphaGen,NoDepthTest,Blend,TexCoordMod,Scale,UScale,VScale,ShiftNoise,Noise,SRange,TRange,"
"Cull,Sort,State,NoCull,ShadowMapGen,Conditions,Vars,DepthWrite,NoColorMask,Portal,LMNoAlpha,"
"TexColorOp,TexStage,TexType,TexFilter,TexGen,UpdateStyle,EvalLight,Style,TexDecal,Tex1Decal,TexBump,"
"RCParam,RCombiner,RShader,TSParam,Reg,Comp,DepthMask,AlphaFunc,Light,LightType,ClipPlane,PlaneS,PlaneT,"
"PolygonOffset,NoLightmap,ShineMap,Turbulence,tcMod,Procedure,TessSize,Spark,Sequence,Maps,Time,Loop,"
"Mask,Public,float,RenderParams,User,"
"rgbGen,blend,map,"
"Translate,Identity,Rotate,RotateX,RotateY,RotateZ,Div,DeformGen,Scroll,UScroll,VScroll,Angle"
"Type,Level,Amp,Phase,Freq,DeformVertexes,FlareSize,NoLight,Const,Start,"
"Matrix,FLOAT,BYTE,Verts,Vertex,Normal,Normals,Color,Texture0,Texture1,Texture2,Texture3,Texture4,TNormals";
const char* sConstants = "Decal,None,Nearest,TwoSided,RCRGBToAlpha,OcclusionTest,NoSet,Replace,FromClient,"
"Opaque,MonitorNoise,Point,Front,Back,Water,TriLinear,"
"MuzzleFlash,FromObj,Modulate,Base,SphereMap,Add,Glare,Additive,Intensity,White,Sin,Cos,Tan,"
"$Diffuse,$None,$Specular,$Whiteimage,$Environment,$Glare,$Opacity,$Flare";
const char* sDirectives = "#define,#elif,#else,#endif,#error,#ifdef,"
"#ifndef,#import,#include,#line,#pragma,#undef";
m_sc.ClearKeywordList();
m_sc.AddKeyword(sKeywords, QColor(0, 0, 255), GRP_KEYWORD);
m_sc.AddKeyword(sConstants, QColor(180, 0, 110), GRP_CONSTANTS);
m_sc.AddKeyword(sDirectives, QColor(160, 0, 160), GRP_DIRECTIVE);
m_sc.SetCommentColor(QColor(0, 128, 128));
m_sc.SetStringColor(QColor(0, 128, 0));
m_bModified = true;
QFont font;
font.setFamily("Courier New");
font.setFixedPitch(true);
font.setPointSize(10);
setFont(font);
setLineWrapMode(NoWrap);
connect(this, &QTextEdit::textChanged, this, &CTextEditorCtrl::OnChange);
}
CTextEditorCtrl::~CTextEditorCtrl()
{
}
// CTextEditorCtrl message handlers
void CTextEditorCtrl::LoadFile(const QString& sFileName)
{
if (m_filename == sFileName)
{
return;
}
m_filename = sFileName;
clear();
CCryFile file(sFileName.toUtf8().data(), "rb");
if (file.Open(sFileName.toUtf8().data(), "rb"))
{
size_t length = file.GetLength();
QByteArray text;
text.resize(length);
file.ReadRaw(text.data(), length);
setPlainText(text);
}
m_bModified = false;
}
//////////////////////////////////////////////////////////////////////////
void CTextEditorCtrl::SaveFile(const QString& sFileName)
{
if (sFileName.isEmpty())
{
return;
}
if (!CFileUtil::OverwriteFile(sFileName.toUtf8().data()))
{
return;
}
QFile file(sFileName);
file.open(QFile::WriteOnly);
file.write(toPlainText().toUtf8());
m_bModified = false;
}
//////////////////////////////////////////////////////////////////////////
void CTextEditorCtrl::OnChange()
{
m_bModified = true;
}
#include <Controls/moc_TextEditorCtrl.cpp>
@@ -0,0 +1,49 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
#pragma once
// CTextEditorCtrl
#if !defined(Q_MOC_RUN)
#include "SyntaxColorizer.h"
#include <QTextEdit>
#endif
class CTextEditorCtrl
: public QTextEdit
{
Q_OBJECT
public:
CTextEditorCtrl(QWidget* pParent = nullptr);
virtual ~CTextEditorCtrl();
void LoadFile(const QString& sFileName);
void SaveFile(const QString& sFileName);
QString GetFilename() const { return m_filename; }
bool IsModified() const { return m_bModified; }
//! Must be called after OnChange message.
void OnChange();
protected:
QString m_filename;
CSyntaxColorizer m_sc;
bool m_bModified;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
@@ -0,0 +1,26 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : implementation file
#include "EditorDefs.h"
#include "TimeOfDaySlider.h"
QString TimeOfDaySlider::hoverValueText(int sliderValue) const
{
return QString::fromLatin1("%1:%2").arg(static_cast<int>(sliderValue / 60)).arg(sliderValue % 60, 2, 10, QLatin1Char('0'));
}
#include <Controls/moc_TimeOfDaySlider.cpp>
@@ -0,0 +1,34 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_TIMEOFDAYSLIDER_H
#define CRYINCLUDE_EDITOR_TIMEOFDAYSLIDER_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzQtComponents/Components/Widgets/Slider.h>
#endif
class TimeOfDaySlider
: public AzQtComponents::SliderInt
{
Q_OBJECT
public:
using AzQtComponents::SliderInt::SliderInt;
protected:
QString hoverValueText(int sliderValue) const override;
};
#endif // CRYINCLUDE_EDITOR_TIMEOFDAYSLIDER_H
@@ -0,0 +1,681 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "TimelineCtrl.h"
// Qt
#include <QPainter>
// Editor
#include "ScopedVariableSetter.h"
#include "GridUtils.h"
static const QColor timeMarkerCol = QColor(255, 0, 255);
static const QColor textCol = QColor(0, 0, 0);
static const QColor ltgrayCol = QColor(110, 110, 110);
QColor InterpolateColor(const QColor& c1, const QColor& c2, float fraction)
{
const int r = (c2.red() - c1.red()) * fraction + c1.red();
const int g = (c2.green() - c1.green()) * fraction + c1.green();
const int b = (c2.blue() - c1.blue()) * fraction + c1.blue();
return QColor(r, g, b);
}
//////////////////////////////////////////////////////////////////////////
TimelineWidget::TimelineWidget(QWidget* parent /* = nullptr */)
: QWidget(parent)
{
setMouseTracking(true);
m_timeRange.start = 0;
m_timeRange.end = 1;
m_timeScale = 1;
m_fTicksTextScale = 1.0f;
m_fTimeMarker = -10;
m_nTicksStep = 100;
m_trackingMode = TRACKING_MODE_NONE;
m_leftOffset = 0;
m_scrollOffset = 0;
m_ticksStep = 10.0f;
m_grid.zoom.x = 100;
m_bIgnoreSetTime = false;
m_pKeyTimeSet = 0;
m_markerStyle = MARKER_STYLE_SECONDS;
m_fps = 30.0f;
m_copyKeyTimes = false;
m_bTrackingSnapToFrames = false;
}
TimelineWidget::~TimelineWidget()
{
}
/////////////////////////////////////////////////////////////////////////////
// CTimelineCtrl message handlers
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::resizeEvent(QResizeEvent* event)
{
Q_UNUSED(event);
m_rcTimeline = rect();
m_grid.rect = m_rcTimeline;
}
//////////////////////////////////////////////////////////////////////////
int TimelineWidget::TimeToClient(float time)
{
return m_grid.WorldToClient(Vec2(time, 0)).x();
}
//////////////////////////////////////////////////////////////////////////
float TimelineWidget::ClientToTime(int x)
{
return m_grid.ClientToWorld(QPoint(x, 0)).x;
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
QRect rcClient = rect();
{
//////////////////////////////////////////////////////////////////////////
// Fill keys background.
//////////////////////////////////////////////////////////////////////////
const QRect rc = rcClient.intersected(event->rect());
painter.fillRect(rc, palette().color(QPalette::Button));
painter.drawRect(rc);
//////////////////////////////////////////////////////////////////////////
m_grid.CalculateGridLines();
DrawTicks(&painter);
}
}
//////////////////////////////////////////////////////////////////////////
float TimelineWidget::SnapTime(float time)
{
double t = floor((double)time * m_ticksStep + 0.5);
t = t / m_ticksStep;
return t;
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::DrawTicks(QPainter* painter)
{
const QRectF rc = rect();
const QPen pOldPen = painter->pen();
const QPen ltgray(QColor(110, 110, 110));
const QPen black(palette().color(QPalette::Normal, QPalette::Text));
const QPen redpen(QColor(255, 0, 255));
// Draw time ticks every tick step seconds.
Range timeRange = m_timeRange;
painter->setPen(ltgray);
switch (m_markerStyle)
{
case MARKER_STYLE_SECONDS:
DrawSecondTicks(painter);
break;
case MARKER_STYLE_FRAMES:
DrawFrameTicks(painter);
break;
}
painter->setPen(redpen);
int x = TimeToClient(m_fTimeMarker);
painter->setBrush(Qt::NoBrush);
painter->drawRect(QRect(QPoint(x - 3, rc.top()), QPoint(x + 2, rc.bottom())));
painter->setPen(redpen);
painter->drawLine(x, rc.top(), x, rc.bottom());
painter->setBrush(Qt::NoBrush);
// Draw vertical line showing current time.
{
int x2 = TimeToClient(m_fTimeMarker);
if (x2 > m_rcTimeline.left() && x2 < m_rcTimeline.right())
{
painter->setPen(QColor(255, 0, 255));
painter->drawLine(x2, 0, x2, m_rcTimeline.bottom());
}
}
// Draw the key times.
painter->setPen(redpen);
painter->setBrush(Qt::NoBrush);
const QPen keySelectedPen(QColor(100, 255, 255));
const QBrush keySelectedBrush(QColor(100, 255, 255));
for (int keyTimeIndex = 0; m_pKeyTimeSet && keyTimeIndex < m_pKeyTimeSet->GetKeyTimeCount(); ++keyTimeIndex)
{
int keyCountBound = __max(m_pKeyTimeSet->GetKeyCountBound(), 1);
int keyCount = __min(m_pKeyTimeSet->GetKeyCount(keyTimeIndex), keyCountBound);
float colorCodeFraction = float(keyCount) / keyCountBound;
const QColor keyMarkerCol = InterpolateColor(Qt::green, Qt::red, colorCodeFraction);
const QPen keyPen(keyMarkerCol);
const QBrush keyBrush(keyMarkerCol);
bool keyTimeSelected = m_pKeyTimeSet && m_pKeyTimeSet->GetKeyTimeSelected(keyTimeIndex);
painter->setBrush(keyTimeSelected ? keySelectedBrush : keyBrush);
painter->setPen(keyTimeSelected ? keySelectedPen : keyPen);
float keyTime = (m_pKeyTimeSet ? m_pKeyTimeSet->GetKeyTime(keyTimeIndex) : 0.0f);
int x2 = TimeToClient(keyTime);
painter->drawRect(QRect(QPoint(x2 - 1, rc.top()), QPoint(x2 + 2, rc.bottom())));
}
painter->setPen(pOldPen);
}
//////////////////////////////////////////////////////////////////////////
Range TimelineWidget::GetVisibleRange() const
{
Range r;
r.start = (m_scrollOffset - m_leftOffset) / m_timeScale;
r.end = r.start + (m_rcTimeline.width()) / m_timeScale;
// Intersect range with global time range.
r = m_timeRange & r;
return r;
}
/////////////////////////////////////////////////////////////////////////////
//Mouse Message Handlers
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::mousePressEvent(QMouseEvent* event)
{
switch (event->button())
{
case Qt::LeftButton:
OnLButtonDown(event->pos(), event->modifiers());
break;
case Qt::RightButton:
OnRButtonDown(event->pos(), event->modifiers());
break;
}
}
void TimelineWidget::mouseReleaseEvent(QMouseEvent* event)
{
switch (event->button())
{
case Qt::LeftButton:
OnLButtonUp(event->pos(), event->modifiers());
break;
case Qt::RightButton:
OnRButtonUp(event->pos(), event->modifiers());
break;
}
}
namespace
{
const float EDITOR_FPS = 30.0f;
float SnapTimeToFrame(float time) {return int((time * EDITOR_FPS) + 0.5f) * (1.0f / EDITOR_FPS); }
}
void TimelineWidget::OnLButtonDown(const QPoint& point, Qt::KeyboardModifiers modifiers)
{
if (m_trackingMode != TRACKING_MODE_NONE)
{
return;
}
Q_EMIT clicked();
int hitKeyTimeIndex = HitKeyTimes(point);
bool autoDeselect = !(modifiers& Qt::ControlModifier) && (m_pKeyTimeSet && ((hitKeyTimeIndex >= 0) ? !m_pKeyTimeSet->GetKeyTimeSelected(hitKeyTimeIndex) : true));
for (int keyTimeIndex = 0; m_pKeyTimeSet && keyTimeIndex < m_pKeyTimeSet->GetKeyTimeCount(); ++keyTimeIndex)
{
bool shouldBeSelected;
if (keyTimeIndex == hitKeyTimeIndex)
{
shouldBeSelected = (modifiers& Qt::ControlModifier) || !((modifiers& Qt::ShiftModifier) && m_pKeyTimeSet->GetKeyTimeSelected(keyTimeIndex));
}
else
{
shouldBeSelected = (!autoDeselect || (modifiers & Qt::ShiftModifier)) && m_pKeyTimeSet->GetKeyTimeSelected(keyTimeIndex);
}
m_pKeyTimeSet->SetKeyTimeSelected(keyTimeIndex, shouldBeSelected);
}
TrackingMode trackingMode = (hitKeyTimeIndex >= 0 ? TRACKING_MODE_MOVE_KEYS : TRACKING_MODE_NONE);
if (trackingMode == TRACKING_MODE_NONE)
{
trackingMode = ((modifiers& Qt::ControlModifier) ? TRACKING_MODE_SELECTION_RANGE : TRACKING_MODE_SET_TIME);
}
StartTracking(trackingMode);
switch (m_trackingMode)
{
case TRACKING_MODE_SET_TIME:
{
if (m_bTrackingSnapToFrames)
{
SetTimeMarker(SnapTimeToFrame(ClientToTime(point.x())));
}
else
{
SetTimeMarker(ClientToTime(point.x()));
}
CScopedVariableSetter<bool> ignoreSetTime(m_bIgnoreSetTime, true);
Q_EMIT startChange();
Q_EMIT change();
}
break;
case TRACKING_MODE_MOVE_KEYS:
m_bChangedKeyTimeSet = false;
m_copyKeyTimes = (modifiers & Qt::ControlModifier ? true : false);
break;
case TRACKING_MODE_NONE:
break;
}
m_lastPoint = point;
update();
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::OnRButtonDown(const QPoint& point, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
{
Q_EMIT clicked();
if (m_trackingMode != TRACKING_MODE_NONE)
{
return;
}
StartTracking(TRACKING_MODE_SET_TIME);
if (m_bTrackingSnapToFrames)
{
SetTimeMarker(SnapTimeToFrame(ClientToTime(point.x())));
}
else
{
SetTimeMarker(ClientToTime(point.x()));
}
CScopedVariableSetter<bool> ignoreSetTime(m_bIgnoreSetTime, true);
Q_EMIT startChange();
Q_EMIT change();
update();
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::OnRButtonUp([[maybe_unused]] const QPoint& point, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
{
switch (m_trackingMode)
{
case TRACKING_MODE_SET_TIME:
{
Q_EMIT endChange();
}
break;
}
if (m_trackingMode != TRACKING_MODE_NONE)
{
StopTracking();
}
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::keyPressEvent(QKeyEvent* event)
{
if (event->matches(QKeySequence::Delete))
{
Q_EMIT deleteRequested();
}
if (event->key() == Qt::Key_Space && m_playCallback)
{
m_playCallback();
}
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::mouseMoveEvent(QMouseEvent* event)
{
switch (m_trackingMode)
{
case TRACKING_MODE_SET_TIME:
{
if (m_bTrackingSnapToFrames)
{
SetTimeMarker(SnapTimeToFrame(ClientToTime(event->x())));
}
else
{
SetTimeMarker(ClientToTime(event->x()));
}
CScopedVariableSetter<bool> ignoreSetTime(m_bIgnoreSetTime, true);
Q_EMIT change();
}
break;
case TRACKING_MODE_MOVE_KEYS:
{
if (m_pKeyTimeSet && !m_bChangedKeyTimeSet)
{
m_bChangedKeyTimeSet = true;
m_pKeyTimeSet->BeginEdittingKeyTimes();
}
const bool altClicked = (Qt::AltModifier & QApplication::queryKeyboardModifiers());
float scale, offset;
float startTime = ClientToTime(m_lastPoint.x());
float endTime = ClientToTime(event->x());
if (altClicked)
{
// Alt was pressed, so we should scale the key times rather than translate.
// Calculate the scaling parameters (ie t1 = t0 * M + C).
scale = 1.0f;
if (fabsf(startTime - m_fTimeMarker) > 0.1)
{
scale = (endTime - m_fTimeMarker) / (startTime - m_fTimeMarker);
}
offset = endTime - startTime * scale;
}
else
{
// Simply move the keys.
offset = endTime - startTime;
scale = 1.0f;
}
MoveSelectedKeyTimes(scale, offset);
}
break;
case TRACKING_MODE_SELECTION_RANGE:
{
float start = min(ClientToTime(m_lastPoint.x()), ClientToTime(event->x()));
float end = max(ClientToTime(m_lastPoint.x()), ClientToTime(event->x()));
SelectKeysInRange(start, end, !(event->modifiers() & Qt::ShiftModifier));
m_lastPoint = event->pos();
update();
}
break;
case TRACKING_MODE_NONE:
break;
}
//m_lastPoint = point;
}
//////////////////////////////////////////////////////////////////////////
QString TimelineWidget::TimeToString(float time)
{
return QString::number(time, 'f', 3);
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::OnLButtonUp([[maybe_unused]] const QPoint& point, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
{
switch (m_trackingMode)
{
case TRACKING_MODE_MOVE_KEYS:
{
if (m_pKeyTimeSet && m_bChangedKeyTimeSet)
{
m_pKeyTimeSet->EndEdittingKeyTimes();
}
}
break;
case TRACKING_MODE_SET_TIME:
{
Q_EMIT endChange();
}
break;
case TRACKING_MODE_NONE:
break;
}
if (m_trackingMode != TRACKING_MODE_NONE)
{
StopTracking();
}
}
///////////////////////////////////////////////////////////////////////////////
void TimelineWidget::StartTracking(TrackingMode trackingMode)
{
m_trackingMode = trackingMode;
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::StopTracking()
{
if (!m_trackingMode)
{
return;
}
m_trackingMode = TRACKING_MODE_NONE;
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::SetTimeMarker(float fTime)
{
if (fTime < m_timeRange.start)
{
fTime = m_timeRange.start;
}
else if (fTime > m_timeRange.end)
{
fTime = m_timeRange.end;
}
if (fTime == m_fTimeMarker || m_bIgnoreSetTime)
{
return;
}
int x0 = TimeToClient(m_fTimeMarker);
int x1 = TimeToClient(fTime);
QRect rc(QPoint(x0, m_rcClient.top()), QPoint(x1, m_rcClient.bottom()));
rc = rc.normalized();
rc.adjust(-5, 0, 5, 0);
update(rc);
m_fTimeMarker = fTime;
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::SetZoom(float fZoom)
{
m_grid.zoom.x = fZoom;
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::SetOrigin(float fOffset)
{
m_grid.origin.x = fOffset;
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::SetKeyTimeSet(IKeyTimeSet* pKeyTimeSet)
{
m_pKeyTimeSet = pKeyTimeSet;
}
//////////////////////////////////////////////////////////////////////////
int TimelineWidget::HitKeyTimes(const QPoint& point)
{
const int threshold = 3;
int hitKeyTimeIndex = -1;
for (int keyTimeIndex = 0; m_pKeyTimeSet && keyTimeIndex < m_pKeyTimeSet->GetKeyTimeCount(); ++keyTimeIndex)
{
float keyTime = (m_pKeyTimeSet ? m_pKeyTimeSet->GetKeyTime(keyTimeIndex) : 0.0f);
int x = TimeToClient(keyTime);
if (abs(point.x() - x) <= threshold)
{
hitKeyTimeIndex = keyTimeIndex;
}
}
return hitKeyTimeIndex;
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::MoveSelectedKeyTimes(float scale, float offset)
{
std::vector<int> indices;
for (int keyTimeIndex = 0; m_pKeyTimeSet && keyTimeIndex < m_pKeyTimeSet->GetKeyTimeCount(); ++keyTimeIndex)
{
if (m_pKeyTimeSet && m_pKeyTimeSet->GetKeyTimeSelected(keyTimeIndex))
{
indices.push_back(keyTimeIndex);
}
}
if (m_pKeyTimeSet)
{
m_pKeyTimeSet->MoveKeyTimes(int(indices.size()), &indices[0], scale, offset, m_copyKeyTimes);
}
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::SelectKeysInRange(float start, float end, bool select)
{
for (int keyTimeIndex = 0; m_pKeyTimeSet && keyTimeIndex < m_pKeyTimeSet->GetKeyTimeCount(); ++keyTimeIndex)
{
float time = (m_pKeyTimeSet ? m_pKeyTimeSet->GetKeyTime(keyTimeIndex) : 0.0f);
if (m_pKeyTimeSet && time >= start && time <= end)
{
m_pKeyTimeSet->SetKeyTimeSelected(keyTimeIndex, select);
}
}
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::SetMarkerStyle(MarkerStyle markerStyle)
{
m_markerStyle = markerStyle;
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::SetFPS(float fps)
{
m_fps = fps;
}
//////////////////////////////////////////////////////////////////////////
void TimelineWidget::DrawSecondTicks(QPainter* painter)
{
const QPen ltgray(QColor(110, 110, 110));
const QPen black(palette().color(QPalette::Normal, QPalette::Text));
const QPen redpen(QColor(255, 0, 255));
for (int gx = m_grid.firstGridLine.x(); gx < m_grid.firstGridLine.x() + m_grid.numGridLines.x() + 1; gx++)
{
painter->setPen(ltgray);
int x = m_grid.GetGridLineX(gx);
if (x < 0)
{
continue;
}
painter->drawLine(m_rcTimeline.left() + x, m_rcTimeline.bottom() - 2, m_rcTimeline.left() + x, m_rcTimeline.bottom() - 4);
//if (gx % 10 == 0)
{
float t = m_grid.GetGridLineXValue(gx);
t = floor(t * 1000.0f + 0.5f) / 1000.0f;
const QString str = QString::number(t * m_fTicksTextScale, 'g');
//t = t / pow(10,precision);
painter->setPen(black);
painter->drawLine(m_rcTimeline.left() + x, m_rcTimeline.bottom() - 2, m_rcTimeline.left() + x, m_rcTimeline.bottom() - 14);
painter->drawText(m_rcTimeline.left() + x + 2, m_rcTimeline.top(), str);
}
}
}
//////////////////////////////////////////////////////////////////////////
class TickDrawer
{
public:
TickDrawer(QPainter* painter, const QRect& rect)
: rect(rect)
, painter(painter)
{
}
void operator()(int frameIndex, int x)
{
if (painter)
{
painter->setPen(QColor(110, 110, 110));
painter->drawLine(rect.left() + x, rect.bottom() - 2, rect.left() + x, rect.bottom() - 4);
{
const QString str = QString::number(frameIndex);
painter->setPen(Qt::black);
painter->drawLine(rect.left() + x, rect.bottom() - 2, rect.left() + x, rect.bottom() - 14);
painter->drawText(rect.left() + x + 2, rect.top(), str);
}
}
}
QRect rect;
QPainter* painter;
};
void TimelineWidget::DrawFrameTicks(QPainter* painter)
{
TickDrawer tickDrawer(painter, m_rcTimeline);
GridUtils::IterateGrid(tickDrawer, 50.0f, m_grid.zoom.x, m_grid.origin.x, m_fps, m_grid.rect.left(), m_grid.rect.right() + 1);
}
void TimelineWidget::SetPlayCallback(const std::function<void()>& callback)
{
m_playCallback = callback;
}
#include <Controls/moc_TimelineCtrl.cpp>
+177
View File
@@ -0,0 +1,177 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_TIMELINECTRL_H
#define CRYINCLUDE_EDITOR_CONTROLS_TIMELINECTRL_H
#pragma once
#if !defined(Q_MOC_RUN)
#include "Range.h"
#include "SplineCtrlEx.h"
#include "Controls/WndGridHelper.h"
#include "Util/fastlib.h"
#endif
// Custom styles for this control.
#define TL_STYLE_AUTO_DELETE 0x0001
#define TL_STYLE_NO_TICKS 0x0002
#define TL_STYLE_NO_TIME_MARKER 0x0004
#define TL_STYLE_NO_TEXT 0x0008
// Notify event sent when current time is change on the timeline control.
#define TLN_START_CHANGE (0x0001)
#define TLN_END_CHANGE (0x0002)
#define TLN_CHANGE (0x0003)
#define TLN_DELETE (0x0004)
class AbstractTimelineWidget
{
public:
virtual void setZoom(float zoom, float origin) = 0;
virtual void update(const QRect& r = QRect()) = 0;
virtual void setGeometry(const QRect& r) = 0;
virtual void SetTimeMarker(float marker) = 0;
};
//////////////////////////////////////////////////////////////////////////
// Timeline control.
//////////////////////////////////////////////////////////////////////////
class TimelineWidget
: public QWidget
, public AbstractTimelineWidget
{
Q_OBJECT
public:
TimelineWidget(QWidget* parent = nullptr);
~TimelineWidget();
void setZoom(float zoom, float origin) override { SetZoom(zoom); SetOrigin(origin); update(); }
void update(const QRect& r = QRect()) override { QWidget::update(r); }
void setGeometry(const QRect& r) override { QWidget::setGeometry(r); }
void SetTimeRange(const Range& r) { m_timeRange = r; }
void SetTimeMarker(float fTime);
float GetTimeMarker() const { return m_fTimeMarker; }
void SetZoom(float fZoom);
void SetOrigin(float fOffset);
void SetKeyTimeSet(IKeyTimeSet* pKeyTimeSet);
void SetTicksTextScale(float fScale) { m_fTicksTextScale = fScale; }
float GetTicksTextScale() const { return m_fTicksTextScale; }
void SetTrackingSnapToFrames(bool bEnable) { m_bTrackingSnapToFrames = bEnable; }
enum MarkerStyle
{
MARKER_STYLE_SECONDS,
MARKER_STYLE_FRAMES
};
void SetMarkerStyle(MarkerStyle markerStyle);
void SetFPS(float fps); // Only referred to if MarkerStyle == MARKER_STYLE_FRAMES.
float GetFPS() const
{
return m_fps;
}
void SetPlayCallback(const std::function<void()>& callback);
Q_SIGNALS:
void deleteRequested();
void clicked();
void startChange();
void change();
void endChange();
protected:
enum TrackingMode
{
TRACKING_MODE_NONE,
TRACKING_MODE_SET_TIME,
TRACKING_MODE_MOVE_KEYS,
TRACKING_MODE_SELECTION_RANGE
};
int HitKeyTimes(const QPoint& point);
void MoveSelectedKeyTimes(float scale, float offset);
void SelectKeysInRange(float start, float end, bool select);
void paintEvent(QPaintEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void OnLButtonDown(const QPoint& point, Qt::KeyboardModifiers modifiers);
void OnLButtonUp(const QPoint& point, Qt::KeyboardModifiers modifiers);
void OnRButtonDown(const QPoint& point, Qt::KeyboardModifiers modifiers);
void OnRButtonUp(const QPoint& point, Qt::KeyboardModifiers modifiers);
void keyPressEvent(QKeyEvent* event);
// Drawing functions
float ClientToTime(int x);
int TimeToClient(float fTime);
void DrawTicks(QPainter* painter);
Range GetVisibleRange() const;
void StartTracking(TrackingMode trackingMode);
void StopTracking();
QString TimeToString(float time);
// Convert time in seconds into the milliseconds.
int ToMillis(float time) { return RoundFloatToInt(time * 1000.0f); };
float MillisToTime(int nMillis) { return nMillis / 1000.0f; }
float SnapTime(float time);
void DrawSecondTicks(QPainter* dc);
void DrawFrameTicks(QPainter* dc);
private:
bool m_bAutoDelete;
QRect m_rcClient;
QRect m_rcTimeline;
float m_fTimeMarker;
float m_fTicksTextScale;
TrackingMode m_trackingMode;
QPoint m_lastPoint;
Range m_timeRange;
float m_timeScale;
int m_scrollOffset;
int m_leftOffset;
// Tick every Nth millisecond.
int m_nTicksStep;
double m_ticksStep;
CWndGridHelper m_grid;
bool m_bIgnoreSetTime;
IKeyTimeSet* m_pKeyTimeSet;
bool m_bChangedKeyTimeSet;
MarkerStyle m_markerStyle;
float m_fps;
bool m_copyKeyTimes;
bool m_bTrackingSnapToFrames;
std::function<void()> m_playCallback;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_TIMELINECTRL_H
+171
View File
@@ -0,0 +1,171 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
// Description : implementation file
#include "EditorDefs.h"
// Editor
#include "CryEditDoc.h"
#include "EditTool.h"
#include "ToolButton.h"
QEditorToolButton::QEditorToolButton(QWidget* parent /* = nullptr */)
: QPushButton(parent)
, m_styleSheet(styleSheet())
, m_toolClass(nullptr)
, m_toolCreated(nullptr)
, m_needDocument(true)
{
setSizePolicy({ QSizePolicy::Expanding, QSizePolicy::Fixed });
connect(this, &QAbstractButton::clicked, this, &QEditorToolButton::OnClicked);
GetIEditor()->RegisterNotifyListener(this);
}
QEditorToolButton::~QEditorToolButton()
{
GetIEditor()->UnregisterNotifyListener(this);
}
void QEditorToolButton::SetToolName(const QString& editToolName, const QString& userDataKey, void* userData)
{
IClassDesc* klass = GetIEditor()->GetClassFactory()->FindClass(editToolName.toUtf8().data());
if (!klass)
{
Warning(QStringLiteral("Editor Tool %1 not registered.").arg(editToolName).toUtf8().data());
return;
}
if (klass->SystemClassID() != ESYSTEM_CLASS_EDITTOOL)
{
Warning(QStringLiteral("Class name %1 is not a valid Edit Tool class.").arg(editToolName).toUtf8().data());
return;
}
QScopedPointer<QObject> o(klass->CreateQObject());
if (!qobject_cast<CEditTool*>(o.data()))
{
Warning(QStringLiteral("Class name %1 is not a valid Edit Tool class.").arg(editToolName).toUtf8().data());
return;
}
SetToolClass(o->metaObject(), userDataKey, userData);
}
//////////////////////////////////////////////////////////////////////////
void QEditorToolButton::SetToolClass(const QMetaObject* toolClass, const QString& userDataKey, void* userData)
{
m_toolClass = toolClass;
m_userData = userData;
if (!userDataKey.isEmpty())
{
m_userDataKey = userDataKey;
}
}
void QEditorToolButton::OnEditorNotifyEvent(EEditorNotifyEvent event)
{
switch (event)
{
case eNotify_OnBeginNewScene:
case eNotify_OnBeginLoad:
case eNotify_OnBeginSceneOpen:
{
if (m_needDocument)
{
setEnabled(false);
}
break;
}
case eNotify_OnEndNewScene:
case eNotify_OnEndLoad:
case eNotify_OnEndSceneOpen:
{
if (m_needDocument)
{
setEnabled(true);
}
break;
}
case eNotify_OnEditToolChange:
{
CEditTool* tool = GetIEditor()->GetEditTool();
if (!tool || tool != m_toolCreated || tool->metaObject() != m_toolClass)
{
m_toolCreated = nullptr;
SetSelected(false);
}
}
default:
break;
}
}
void QEditorToolButton::OnClicked()
{
if (!m_toolClass)
{
return;
}
if (m_needDocument && !GetIEditor()->GetDocument()->IsDocumentReady())
{
return;
}
CEditTool* tool = GetIEditor()->GetEditTool();
if (tool && tool->IsMoveToObjectModeAfterEnd() && tool->metaObject() == m_toolClass && tool == m_toolCreated)
{
GetIEditor()->SetEditTool(nullptr);
SetSelected(false);
}
else
{
CEditTool* newTool = qobject_cast<CEditTool*>(m_toolClass->newInstance());
if (!newTool)
{
return;
}
m_toolCreated = newTool;
SetSelected(true);
if (m_userData)
{
newTool->SetUserData(m_userDataKey.toUtf8().data(), (void*)m_userData);
}
update();
// Must be last function, can delete this.
GetIEditor()->SetEditTool(newTool);
}
}
void QEditorToolButton::SetSelected(bool selected)
{
if (selected)
{
setStyleSheet(QStringLiteral("QPushButton { background-color: palette(highlight); color: palette(highlighted-text); }"));
}
else
{
setStyleSheet(m_styleSheet);
}
}
#include <Controls/moc_ToolButton.cpp>
+60
View File
@@ -0,0 +1,60 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_TOOLBUTTON_H
#define CRYINCLUDE_EDITOR_CONTROLS_TOOLBUTTON_H
#pragma once
// ToolButton.h : header file
//
#if !defined(Q_MOC_RUN)
#include <AzCore/PlatformDef.h>
#include <QPushButton>
#endif
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
class SANDBOX_API QEditorToolButton
: public QPushButton
, public IEditorNotifyListener
{
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
Q_OBJECT
// Construction
public:
QEditorToolButton(QWidget* parent = nullptr);
virtual ~QEditorToolButton();
void SetToolClass(const QMetaObject* toolClass, const QString& userDataKey = 0, void* userData = nullptr);
void SetToolName(const QString& editToolName, const QString& userDataKey = 0, void* userData = nullptr);
// Set if this tool button relies on a loaded level / ready document. By default every tool button only works if a level is loaded.
// However some tools are also used without a loaded level (e.g. UI Emulator)
void SetNeedDocument(bool needDocument) { m_needDocument = needDocument; }
void SetSelected(bool selected);
void OnEditorNotifyEvent(EEditorNotifyEvent event) override;
protected:
void OnClicked();
const QString m_styleSheet;
//! Tool associated with this button.
const QMetaObject* m_toolClass;
CEditTool* m_toolCreated;
QString m_userDataKey;
void* m_userData;
bool m_needDocument;
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_TOOLBUTTON_H
@@ -0,0 +1,326 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_TREECTRLUTILS_H
#define CRYINCLUDE_EDITOR_CONTROLS_TREECTRLUTILS_H
#pragma once
#include <iterator>
namespace TreeCtrlUtils
{
template <typename P>
class TreeItemIterator
: public P
{
public:
typedef P Traits;
//iterator traits, required by STL
typedef ptrdiff_t difference_type;
typedef HTREEITEM value_type;
typedef HTREEITEM* pointer;
typedef HTREEITEM& reference;
typedef std::forward_iterator_tag iterator_category;
TreeItemIterator()
: pCtrl(0)
, hItem(0) {}
explicit TreeItemIterator(const P& traits)
: P(traits)
, pCtrl(0)
, hItem(0) {}
TreeItemIterator(const TreeItemIterator& other)
: P(other)
, pCtrl(other.pCtrl)
, hItem(other.hItem) {}
TreeItemIterator(CTreeCtrl* pCtrl, HTREEITEM hItem)
: pCtrl(pCtrl)
, hItem(hItem) {}
TreeItemIterator(CTreeCtrl* pCtrl, HTREEITEM hItem, const P& traits)
: P(traits)
, pCtrl(pCtrl)
, hItem(hItem) {}
HTREEITEM operator*() {return hItem; }
bool operator==(const TreeItemIterator& other) const {return pCtrl == other.pCtrl && hItem == other.hItem; }
bool operator!=(const TreeItemIterator& other) const {return pCtrl != other.pCtrl || hItem != other.hItem; }
TreeItemIterator& operator++()
{
HTREEITEM hNextItem = 0;
if (RecurseToChildren(hItem))
{
hNextItem = (pCtrl ? pCtrl->GetChildItem(hItem) : 0);
}
while (pCtrl && hItem && !hNextItem)
{
hNextItem = pCtrl->GetNextSiblingItem(hItem);
if (!hNextItem)
{
hItem = pCtrl->GetParentItem(hItem);
}
}
hItem = hNextItem;
return *this;
}
TreeItemIterator operator++(int) {TreeItemIterator old = *this; ++(*this); return old; }
CTreeCtrl* pCtrl;
HTREEITEM hItem;
};
class NonRecursiveTreeItemIteratorTraits
{
public:
bool RecurseToChildren(HTREEITEM hItem) {return false; }
};
typedef TreeItemIterator<NonRecursiveTreeItemIteratorTraits> NonRecursiveTreeItemIterator;
class RecursiveTreeItemIteratorTraits
{
public:
bool RecurseToChildren(HTREEITEM hItem) {return true; }
};
typedef TreeItemIterator<RecursiveTreeItemIteratorTraits> RecursiveTreeItemIterator;
inline RecursiveTreeItemIterator BeginTreeItemsRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
if (hItem == 0)
{
hItem = (pCtrl ? pCtrl->GetRootItem() : 0);
}
return RecursiveTreeItemIterator(pCtrl, hItem);
}
inline RecursiveTreeItemIterator EndTreeItemsRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
HTREEITEM hEndItem = 0;
HTREEITEM hParent = hItem;
do
{
if (hParent)
{
hEndItem = pCtrl->GetNextSiblingItem(hParent);
}
hParent = (pCtrl && hParent ? pCtrl->GetParentItem(hParent) : 0);
}
while (hParent && !hEndItem);
return RecursiveTreeItemIterator(pCtrl, hEndItem);
}
inline NonRecursiveTreeItemIterator BeginTreeItemsNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
if (hItem == 0)
{
hItem = (pCtrl ? pCtrl->GetRootItem() : 0);
}
if (hItem)
{
hItem = pCtrl->GetChildItem(hItem);
}
return NonRecursiveTreeItemIterator(pCtrl, hItem);
}
inline NonRecursiveTreeItemIterator EndTreeItemsNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
HTREEITEM hEndItem = 0;
HTREEITEM hParent = 0;
while (hParent && !hEndItem)
{
hParent = (pCtrl && hItem ? pCtrl->GetParentItem(hItem) : 0);
if (hParent)
{
hEndItem = pCtrl->GetNextSiblingItem(hParent);
}
}
return NonRecursiveTreeItemIterator(pCtrl, hEndItem);
}
template <typename T, typename P>
class TreeItemDataIterator
{
public:
typedef T Type;
typedef TreeItemIterator<P> InternalIterator;
//iterator traits, required by STL
typedef ptrdiff_t difference_type;
typedef Type* value_type;
typedef Type** pointer;
typedef Type*& reference;
typedef std::forward_iterator_tag iterator_category;
TreeItemDataIterator() {}
TreeItemDataIterator(const TreeItemDataIterator& other)
: iterator(other.iterator) {AdvanceToValidIterator(); }
explicit TreeItemDataIterator(const InternalIterator& iterator)
: iterator(iterator) {AdvanceToValidIterator(); }
Type* operator*() {return reinterpret_cast<Type*>(iterator.pCtrl->GetItemData(iterator.hItem)); }
bool operator==(const TreeItemDataIterator& other) const {return iterator == other.iterator; }
bool operator!=(const TreeItemDataIterator& other) const {return iterator != other.iterator; }
HTREEITEM GetTreeItem() {return iterator.hItem; }
TreeItemDataIterator& operator++()
{
++iterator;
AdvanceToValidIterator();
return *this;
}
TreeItemDataIterator operator++(int) {TreeItemDataIterator old = *this; ++(*this); return old; }
private:
void AdvanceToValidIterator()
{
while (iterator.pCtrl && iterator.hItem && !iterator.pCtrl->GetItemData(iterator.hItem))
{
++iterator;
}
}
InternalIterator iterator;
};
template <typename T>
class RecursiveItemDataIteratorType
{
public: typedef TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits> type;
};
template <typename T>
inline TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits> BeginTreeItemDataRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits>(BeginTreeItemsRecursive(pCtrl, hItem));
}
template <typename T>
inline TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits> EndTreeItemDataRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, RecursiveTreeItemIteratorTraits>(EndTreeItemsRecursive(pCtrl, hItem));
}
template <typename T>
class NonRecursiveItemDataIteratorType
{
typedef TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits> type;
};
template <typename T>
inline TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits> BeginTreeItemDataNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits>(BeginTreeItemsNonRecursive(pCtrl, hItem));
}
template <typename T>
inline TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits> EndTreeItemDataNonRecursive(CTreeCtrl* pCtrl, HTREEITEM hItem = 0)
{
return TreeItemDataIterator<T, NonRecursiveTreeItemIteratorTraits>(EndTreeItemsNonRecursive(pCtrl, hItem));
}
class SelectedTreeItemIterator
{
public:
SelectedTreeItemIterator()
: pCtrl(0)
, hItem(0) {}
SelectedTreeItemIterator(const SelectedTreeItemIterator& other)
: pCtrl(other.pCtrl)
, hItem(other.hItem) {}
SelectedTreeItemIterator(CXTTreeCtrl* pCtrl, HTREEITEM hItem)
: pCtrl(pCtrl)
, hItem(hItem) {}
HTREEITEM operator*() {return hItem; }
bool operator==(const SelectedTreeItemIterator& other) const {return pCtrl == other.pCtrl && hItem == other.hItem; }
bool operator!=(const SelectedTreeItemIterator& other) const {return pCtrl != other.pCtrl || hItem != other.hItem; }
SelectedTreeItemIterator& operator++()
{
hItem = (pCtrl ? pCtrl->GetNextSelectedItem(hItem) : 0);
return *this;
}
SelectedTreeItemIterator operator++(int) {SelectedTreeItemIterator old = *this; ++(*this); return old; }
CXTTreeCtrl* pCtrl;
HTREEITEM hItem;
};
SelectedTreeItemIterator BeginSelectedTreeItems(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemIterator(pCtrl, (pCtrl ? pCtrl->GetFirstSelectedItem() : 0));
}
SelectedTreeItemIterator EndSelectedTreeItems(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemIterator(pCtrl, 0);
}
template <typename T>
class SelectedTreeItemDataIterator
{
public:
typedef T Type;
typedef SelectedTreeItemIterator InternalIterator;
SelectedTreeItemDataIterator() {}
SelectedTreeItemDataIterator(const SelectedTreeItemDataIterator& other)
: iterator(other.iterator) {AdvanceToValidIterator(); }
explicit SelectedTreeItemDataIterator(const InternalIterator& iterator)
: iterator(iterator) {AdvanceToValidIterator(); }
Type* operator*() {return reinterpret_cast<Type*>(iterator.pCtrl->GetItemData(iterator.hItem)); }
bool operator==(const SelectedTreeItemDataIterator& other) const {return iterator == other.iterator; }
bool operator!=(const SelectedTreeItemDataIterator& other) const {return iterator != other.iterator; }
HTREEITEM GetTreeItem() {return iterator.hItem; }
SelectedTreeItemDataIterator& operator++()
{
++iterator;
AdvanceToValidIterator();
return *this;
}
SelectedTreeItemDataIterator operator++(int) {SelectedTreeItemDataIterator old = *this; ++(*this); return old; }
private:
void AdvanceToValidIterator()
{
while (iterator.pCtrl && iterator.hItem && !iterator.pCtrl->GetItemData(iterator.hItem))
{
++iterator;
}
}
InternalIterator iterator;
};
template <typename T>
SelectedTreeItemDataIterator<T> BeginSelectedTreeItemData(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemDataIterator<T>(BeginSelectedTreeItems(pCtrl));
}
template <typename T>
SelectedTreeItemDataIterator<T> EndSelectedTreeItemData(CXTTreeCtrl* pCtrl)
{
return SelectedTreeItemDataIterator<T>(EndSelectedTreeItems(pCtrl));
}
}
#endif // CRYINCLUDE_EDITOR_CONTROLS_TREECTRLUTILS_H
@@ -0,0 +1,178 @@
/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_EDITOR_CONTROLS_WNDGRIDHELPER_H
#define CRYINCLUDE_EDITOR_CONTROLS_WNDGRIDHELPER_H
#pragma once
#include <QPoint>
#include <QRect>
#include "Cry_Vector2.h"
//////////////////////////////////////////////////////////////////////////
class CWndGridHelper
{
public:
Vec2 zoom;
Vec2 origin;
Vec2 step;
Vec2 pixelsPerGrid;
int nMajorLines;
QRect rect;
QPoint nMinPixelsPerGrid;
QPoint nMaxPixelsPerGrid;
//////////////////////////////////////////////////////////////////////////
QPoint firstGridLine;
QPoint numGridLines;
//////////////////////////////////////////////////////////////////////////
CWndGridHelper()
{
zoom = Vec2(1, 1);
step = Vec2(10, 10);
pixelsPerGrid = Vec2(10, 10);
origin = Vec2(0, 0);
nMajorLines = 10;
nMinPixelsPerGrid = QPoint(50, 10);
nMaxPixelsPerGrid = QPoint(100, 20);
firstGridLine = QPoint(0, 0);
numGridLines = QPoint(0, 0);
}
//////////////////////////////////////////////////////////////////////////
Vec2 ClientToWorld(const QPoint& point)
{
Vec2 v;
v.x = (point.x() - rect.left()) / zoom.x + origin.x;
v.y = (point.y() - rect.top()) / zoom.y + origin.y;
return v;
}
//////////////////////////////////////////////////////////////////////////
QPoint WorldToClient(Vec2 v)
{
QPoint p(aznumeric_cast<int>(floor((v.x - origin.x) * zoom.x + 0.5f) + rect.left()),
aznumeric_cast<int>(floor((v.y - origin.y) * zoom.y + 0.5f) + rect.top()));
return p;
}
void SetOrigin(Vec2 neworigin)
{
origin = neworigin;
}
void SetZoom(Vec2 newzoom)
{
zoom = newzoom;
}
//////////////////////////////////////////////////////////////////////////
void SetZoom(Vec2 newzoom, const QPoint& center)
{
if (newzoom.x < 0.01f)
{
newzoom.x = 0.01f;
}
if (newzoom.y < 0.01f)
{
newzoom.y = 0.01f;
}
Vec2 prevz = zoom;
// Zoom to mouse position.
float ofsx = origin.x;
float ofsy = origin.y;
Vec2 z1 = zoom;
Vec2 z2 = newzoom;
zoom = newzoom;
// Calculate new offset to center zoom on mouse.
float x2 = aznumeric_cast<float>(center.x() - rect.left());
float y2 = aznumeric_cast<float>(center.y() - rect.top());
ofsx = -(x2 / z2.x - x2 / z1.x - ofsx);
ofsy = -(y2 / z2.y - y2 / z1.y - ofsy);
origin.x = ofsx;
origin.y = ofsy;
}
void CalculateGridLines()
{
pixelsPerGrid.x = zoom.x;
pixelsPerGrid.y = zoom.y;
step = Vec2(1.00f, 1.00f);
nMajorLines = 2;
int griditers;
if (pixelsPerGrid.x <= nMinPixelsPerGrid.x())
{
griditers = 0;
while (pixelsPerGrid.x <= nMinPixelsPerGrid.x() && griditers++ < 1000)
{
step.x = step.x * nMajorLines;
pixelsPerGrid.x = step.x * zoom.x;
}
}
else
{
griditers = 0;
while (pixelsPerGrid.x >= nMaxPixelsPerGrid.x() && griditers++ < 1000)
{
step.x = step.x / nMajorLines;
pixelsPerGrid.x = step.x * zoom.x;
}
}
if (pixelsPerGrid.y <= nMinPixelsPerGrid.y())
{
griditers = 0;
while (pixelsPerGrid.y <= nMinPixelsPerGrid.y() && griditers++ < 1000)
{
step.y = step.y * nMajorLines;
pixelsPerGrid.y = step.y * zoom.y;
}
}
else
{
griditers = 0;
while (pixelsPerGrid.y >= nMaxPixelsPerGrid.y() && griditers++ < 1000)
{
step.y = step.y / nMajorLines;
pixelsPerGrid.y = step.y * zoom.y;
}
}
firstGridLine.rx() = aznumeric_cast<int>(origin.x / step.x);
firstGridLine.ry() = aznumeric_cast<int>(origin.y / step.y);
numGridLines.rx() = aznumeric_cast<int>((rect.width() / zoom.x) / step.x + 1);
numGridLines.ry() = aznumeric_cast<int>((rect.height() / zoom.y) / step.y + 1);
}
int GetGridLineX(int nGridLineX) const
{
return aznumeric_cast<int>(floor((nGridLineX * step.x - origin.x) * zoom.x + 0.5f));
}
int GetGridLineY(int nGridLineY) const
{
return aznumeric_cast<int>(floor((nGridLineY * step.y - origin.y) * zoom.y + 0.5f));
}
float GetGridLineXValue(int nGridLineX) const
{
return (nGridLineX * step.x);
}
float GetGridLineYValue(int nGridLineY) const
{
return (nGridLineY * step.y);
}
};
#endif // CRYINCLUDE_EDITOR_CONTROLS_WNDGRIDHELPER_H