git mv Code\Sandbox\Editor Code/Editor
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// 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,87 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// 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,927 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
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)
|
||||
{
|
||||
//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,167 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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 AZStd::function<void(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
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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>
|
||||
@@ -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,542 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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(AZStd::bind(OnConsoleVariableUpdated, AZStd::placeholders::_1));
|
||||
}
|
||||
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,110 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,502 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,119 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,47 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,32 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,446 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,79 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,566 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,96 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,66 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,43 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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>
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#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,63 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#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,527 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#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();
|
||||
|
||||
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,88 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#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,641 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#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()
|
||||
{
|
||||
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,147 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#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,130 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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()
|
||||
{
|
||||
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,77 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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,37 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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 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 ColorCurveHandler());
|
||||
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew FloatCurveHandler());
|
||||
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, aznew MotionPropertyWidgetHandler());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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,257 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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 "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 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,232 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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 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 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,198 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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"
|
||||
|
||||
|
||||
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>
|
||||
|
||||
QWidget* FloatCurveHandler::CreateGUI(QWidget *pParent)
|
||||
{
|
||||
CSplineCtrl *cSpline = new CSplineCtrl(pParent);
|
||||
cSpline->SetUpdateCallback(AZStd::bind(&FloatCurveHandler::OnSplineChange, this, AZStd::placeholders::_1));
|
||||
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,97 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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 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,59 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "EditorDefs.h"
|
||||
|
||||
#include "PropertyMotionCtrl.h"
|
||||
|
||||
// AzToolsFramework
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
|
||||
|
||||
QWidget* MotionPropertyWidgetHandler::CreateGUI(QWidget* pParent)
|
||||
{
|
||||
AzToolsFramework::PropertyAssetCtrl* newCtrl = aznew AzToolsFramework::PropertyAssetCtrl(pParent);
|
||||
connect(
|
||||
newCtrl, &AzToolsFramework::PropertyAssetCtrl::OnAssetIDChanged, this, [newCtrl]([[maybe_unused]] AZ::Data::AssetId newAssetId) {
|
||||
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl);
|
||||
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(
|
||||
&AzToolsFramework::PropertyEditorGUIMessages::Bus::Handler::OnEditingFinished, newCtrl);
|
||||
});
|
||||
return newCtrl;
|
||||
}
|
||||
|
||||
void MotionPropertyWidgetHandler::ConsumeAttribute(
|
||||
[[maybe_unused]] AzToolsFramework::PropertyAssetCtrl* GUI, [[maybe_unused]] AZ::u32 attrib,
|
||||
[[maybe_unused]] AzToolsFramework::PropertyAttributeReader* attrValue, [[maybe_unused]] const char* debugName)
|
||||
{
|
||||
}
|
||||
|
||||
void MotionPropertyWidgetHandler::WriteGUIValuesIntoProperty(
|
||||
[[maybe_unused]] size_t index, [[maybe_unused]] AzToolsFramework::PropertyAssetCtrl* GUI, property_t& instance,
|
||||
[[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
|
||||
{
|
||||
CReflectedVarMotion val;
|
||||
val.m_motion = GUI->GetCurrentAssetHint();
|
||||
val.m_assetId = GUI->GetSelectedAssetID();
|
||||
instance = static_cast<property_t>(val);
|
||||
}
|
||||
|
||||
bool MotionPropertyWidgetHandler::ReadValuesIntoGUI(
|
||||
[[maybe_unused]] size_t index, [[maybe_unused]] AzToolsFramework::PropertyAssetCtrl* GUI, const property_t& instance,
|
||||
[[maybe_unused]] AzToolsFramework::InstanceDataNode* node)
|
||||
{
|
||||
static const AZ::Data::AssetType emotionFXMotionAssetType(
|
||||
"{00494B8E-7578-4BA2-8B28-272E90680787}"); // from MotionAsset.h in EMotionFX Gem
|
||||
|
||||
GUI->blockSignals(true);
|
||||
GUI->SetSelectedAssetID(instance.m_assetId);
|
||||
GUI->SetCurrentAssetType(emotionFXMotionAssetType);
|
||||
GUI->blockSignals(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#include <Controls/ReflectedPropertyControl/moc_PropertyMotionCtrl.cpp>
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include "ReflectedVar.h"
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/base.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyAssetCtrl.hxx>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
|
||||
#include <QPointer>
|
||||
#include <QWidget>
|
||||
#endif
|
||||
|
||||
class MotionPropertyWidgetHandler : QObject,
|
||||
public AzToolsFramework::PropertyHandler<CReflectedVarMotion, AzToolsFramework::PropertyAssetCtrl>
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
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(AzToolsFramework::PropertyAssetCtrl* widget) override
|
||||
{
|
||||
return widget->GetFirstInTabOrder();
|
||||
}
|
||||
|
||||
virtual QWidget* GetLastInTabOrder(AzToolsFramework::PropertyAssetCtrl* widget) override
|
||||
{
|
||||
return widget->GetLastInTabOrder();
|
||||
}
|
||||
|
||||
virtual void UpdateWidgetInternalTabbing(AzToolsFramework::PropertyAssetCtrl* widget) override
|
||||
{
|
||||
widget->UpdateTabOrder();
|
||||
}
|
||||
|
||||
virtual QWidget* CreateGUI(QWidget* pParent) override;
|
||||
virtual void ConsumeAttribute(
|
||||
AzToolsFramework::PropertyAssetCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue,
|
||||
const char* debugName) override;
|
||||
virtual void WriteGUIValuesIntoProperty(
|
||||
size_t index, AzToolsFramework::PropertyAssetCtrl* GUI, property_t& instance, AzToolsFramework::InstanceDataNode* node) override;
|
||||
virtual bool ReadValuesIntoGUI(
|
||||
size_t index, AzToolsFramework::PropertyAssetCtrl* GUI, const property_t& instance,
|
||||
AzToolsFramework::InstanceDataNode* node) override;
|
||||
};
|
||||
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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:
|
||||
newPath.replace("\\\\", "/");
|
||||
}
|
||||
switch (m_propertyType)
|
||||
{
|
||||
case ePropertyTexture:
|
||||
case ePropertyModel:
|
||||
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,116 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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,92 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// 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, ReflectedPropertyControl::UpdateVarCallback* updCallback, const char* category)
|
||||
{
|
||||
assert(vb);
|
||||
|
||||
m_varBlock = vb;
|
||||
|
||||
RemoveAllItems();
|
||||
m_varBlock = vb;
|
||||
AddVarBlock(m_varBlock, category);
|
||||
|
||||
SetUpdateCallback(AZStd::bind(&ReflectedPropertiesPanel::OnPropertyChanged, this, AZStd::placeholders::_1));
|
||||
|
||||
// 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, 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(AZStd::bind(&ReflectedPropertiesPanel::OnPropertyChanged, this, AZStd::placeholders::_1));
|
||||
|
||||
// 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)->operator()(pVar);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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, ReflectedPropertyControl::UpdateVarCallback* func = nullptr, const char* category = nullptr);
|
||||
|
||||
void SetVarBlock(class CVarBlock* vb, 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,375 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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 AZStd::function<void(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 AZStd::function<void(IVariable*)> UpdateVarCallback;
|
||||
//! When item change, update object.
|
||||
typedef AZStd::function<void(IVariable*)> UpdateObjectCallback;
|
||||
//! When selection changes, this callback fired variable that changed.
|
||||
typedef AZStd::function<void(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 AZStd::function<void()> Callback;
|
||||
|
||||
QString m_text;
|
||||
Callback m_callback;
|
||||
|
||||
SCustomPopupItem(const QString& text, const Callback& callback)
|
||||
: m_text(text)
|
||||
, m_callback(callback) {}
|
||||
};
|
||||
|
||||
struct SCustomPopupMenu
|
||||
{
|
||||
typedef AZStd::function<void(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 AZStd::function<void(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,685 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
m_onSetCallback = AZStd::bind(&ReflectedPropertyItem::OnVariableChange, this, AZStd::placeholders::_1);
|
||||
m_onSetEnumCallback = AZStd::bind(&ReflectedPropertyItem::OnVariableEnumChange, this, AZStd::placeholders::_1);
|
||||
}
|
||||
|
||||
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(&m_onSetCallback);
|
||||
m_pVariable->AddOnSetEnumCallback(&m_onSetEnumCallback);
|
||||
|
||||
// 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 ePropertyEquip:
|
||||
case ePropertyReverbPreset:
|
||||
case ePropertyGameToken:
|
||||
case ePropertyMissionObj:
|
||||
case ePropertySequence:
|
||||
case ePropertySequenceId:
|
||||
case ePropertyLocalString:
|
||||
case ePropertyLightAnimation:
|
||||
case ePropertyParticleName:
|
||||
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(&m_onSetCallback);
|
||||
m_pVariable->RemoveOnSetEnumCallback(&m_onSetEnumCallback);
|
||||
}
|
||||
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:
|
||||
value.replace('\\', '/');
|
||||
break;
|
||||
}
|
||||
|
||||
// correct the length of value
|
||||
switch (m_type)
|
||||
{
|
||||
case ePropertyTexture:
|
||||
case ePropertyModel:
|
||||
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 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
IVariable::OnSetCallback m_onSetCallback;
|
||||
IVariable::OnSetEnumCallback m_onSetEnumCallback;
|
||||
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
|
||||
};
|
||||
|
||||
typedef _smart_ptr<ReflectedPropertyItem> ReflectedPropertyItemPtr;
|
||||
|
||||
|
||||
#endif // CRYINCLUDE_EDITOR_UTILS_REFLECTEDPROPERTYITEM_H
|
||||
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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 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);
|
||||
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,429 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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,579 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
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,316 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,835 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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)
|
||||
{
|
||||
//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>
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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 AZStd::function<void(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
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,92 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "EditorDefs.h"
|
||||
|
||||
#include "TextEditorCtrl.h"
|
||||
|
||||
|
||||
// CTextEditorCtrl
|
||||
CTextEditorCtrl::CTextEditorCtrl(QWidget* pParent)
|
||||
: QTextEdit(pParent)
|
||||
{
|
||||
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,41 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
|
||||
#define CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
|
||||
#pragma once
|
||||
|
||||
// CTextEditorCtrl
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#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;
|
||||
bool m_bModified;
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_EDITOR_CONTROLS_TEXTEDITORCTRL_H
|
||||
@@ -0,0 +1,676 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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>
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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,173 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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
|
||||
Reference in New Issue
Block a user