1075 lines
23 KiB
C++
Executable File
1075 lines
23 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* Topic Designer for the Script Editor - Set Geometry & Z-Index, delete Topics, etc.
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*
|
|
* Created by Mike Wille, SLSE, 2017-06-09
|
|
*/
|
|
|
|
#include "topic_designer.h"
|
|
|
|
#include <QDebug>
|
|
#include <QPaintEvent>
|
|
#include <QPainter>
|
|
#include <QPen>
|
|
#include <QBrush>
|
|
#include <QColor>
|
|
#include <QPoint>
|
|
#include <QRect>
|
|
#include <QSize>
|
|
#include <QCursor>
|
|
#include <QTimer>
|
|
#include <QScrollArea>
|
|
#include <QScrollBar>
|
|
|
|
|
|
namespace PresentationManagement
|
|
{
|
|
|
|
static const double minVal = 0.01f;
|
|
static const double maxVal = 0.98f;
|
|
static int handleSize = 8;
|
|
|
|
|
|
TopicDesigner::TopicDesigner(QWidget *parent, QWidget *previewWidget)
|
|
: QWidget(parent)
|
|
, m_selectedTopicId(-1)
|
|
, m_lowestImportIndex(-1)
|
|
, m_currentResizeHandle(HandleType::None)
|
|
, m_resizeHandleActive(HandleType::None)
|
|
, m_drawWidgetPreviewContent(previewWidget)
|
|
, m_scrollArea(new QScrollArea(parent))
|
|
{
|
|
setFocusPolicy(Qt::StrongFocus);
|
|
setMouseTracking(true);
|
|
|
|
m_scrollArea->setWidget(m_drawWidgetPreviewContent);
|
|
|
|
for (QScrollBar *sb : findChildren<QScrollBar *>())
|
|
{
|
|
qCritical() << "isVisible?" << sb->isVisible() << "" << sb;
|
|
}
|
|
|
|
// TESTS
|
|
|
|
// int idx = 0;
|
|
// int id = 0;
|
|
// double x = 0.2f;
|
|
// double y = 0.2f;
|
|
// double w = 0.1f;
|
|
// double h = 0.1f;
|
|
|
|
|
|
// QVector<LtsScriptTimeline::TrackEntry *> topicsList;
|
|
|
|
// {
|
|
// TopicData data = new LtsScriptTimeline::TrackEntry(LtsScriptTimeline::TrackEntryType::Audio);
|
|
// te->index = idx++;
|
|
// te->topicId = id++;
|
|
// te->x = x+=0.01f;
|
|
// te->y = y+=0.01f;
|
|
// te->w = w+=0.01f;
|
|
// te->h = h+=0.01f;
|
|
|
|
// topicsList.append(te);
|
|
// }
|
|
|
|
// {
|
|
// TopicData data = new LtsScriptTimeline::TrackEntry(LtsScriptTimeline::TrackEntryType::Audio);
|
|
// te->index = idx++;
|
|
// te->topicId = id++;
|
|
// te->x = x+=0.01f;
|
|
// te->y = y+=0.01f;
|
|
// te->w = w+=0.01f;
|
|
// te->h = h+=0.01f;
|
|
|
|
// topicsList.append(te);
|
|
// }
|
|
|
|
// {
|
|
// TopicData data = new LtsScriptTimeline::TrackEntry(LtsScriptTimeline::TrackEntryType::Audio);
|
|
// te->index = idx++;
|
|
// te->topicId = id++;
|
|
// te->x = x+=0.01f;
|
|
// te->y = y+=0.01f;
|
|
// te->w = w+=0.01f;
|
|
// te->h = h+=0.01f;
|
|
|
|
// topicsList.append(te);
|
|
// }
|
|
|
|
// {
|
|
// TopicData data = new LtsScriptTimeline::TrackEntry(LtsScriptTimeline::TrackEntryType::Audio);
|
|
// te->index = idx++;
|
|
// m_selectedTopic = te->topicId = id++;
|
|
// te->x = x+=0.01f;
|
|
// te->y = y+=0.01f;
|
|
// te->w = w+=0.01f;
|
|
// te->h = h+=0.01f;
|
|
|
|
// topicsList.append(te);
|
|
// }
|
|
|
|
// loadTopicList(topicsList);
|
|
|
|
QTimer *timer = new QTimer(this);
|
|
timer->setInterval(1000);
|
|
connect(timer, &QTimer::timeout, this, [this]()
|
|
{
|
|
raise();
|
|
});
|
|
timer->start();
|
|
}
|
|
|
|
TopicDesigner::~TopicDesigner()
|
|
{
|
|
delete m_scrollArea;
|
|
}
|
|
|
|
int TopicDesigner::heightForWidth(int width) const
|
|
{
|
|
int result = width / 4;
|
|
//qCritical() << "h4w width" << width << "result" << result;
|
|
return result;
|
|
}
|
|
|
|
|
|
QVector<TopicEditorBase::TopicData> TopicDesigner::exportTopicList() const
|
|
{
|
|
QVector<TopicEditorBase::TopicData> result;
|
|
int lowestIndex = m_lowestImportIndex;
|
|
for (auto i = m_topicDataList.begin(); i != m_topicDataList.end(); ++i)
|
|
{
|
|
TopicEditorBase::TopicData data = *i;
|
|
|
|
data.index = lowestIndex++;
|
|
result.append(data);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
void TopicDesigner::selectTopic(int topicId)
|
|
{
|
|
if (topicId < 0)
|
|
unselect();
|
|
else
|
|
m_selectedTopicId = topicId;
|
|
|
|
update();
|
|
//emit signalChangesMade();
|
|
}
|
|
|
|
|
|
void TopicDesigner::loadTopicList(const QVector<TopicEditorBase::TopicData> &topicsList)
|
|
{
|
|
m_lowestImportIndex = -1;
|
|
int zIndex = -1;
|
|
|
|
// sort: find HIGHEST & LOWEST index
|
|
for (auto i = topicsList.constBegin(); i != topicsList.constEnd(); ++i)
|
|
{
|
|
TopicEditorBase::TopicData data = *i;
|
|
int teZIndex = data.index;
|
|
if (zIndex < teZIndex || zIndex == -1)
|
|
zIndex = teZIndex;
|
|
|
|
if (m_lowestImportIndex > teZIndex || m_lowestImportIndex == -1)
|
|
m_lowestImportIndex = teZIndex;
|
|
}
|
|
|
|
m_topicDataList.clear();
|
|
while (zIndex >= -1)
|
|
{
|
|
for (auto i = topicsList.constBegin(); i != topicsList.constEnd(); ++i)
|
|
{
|
|
TopicEditorBase::TopicData data = *i;
|
|
if (data.index == zIndex)
|
|
{
|
|
checkConstraints(data); // check first, before adding
|
|
m_topicDataList.prepend(data);
|
|
}
|
|
}
|
|
zIndex--;
|
|
}
|
|
}
|
|
|
|
|
|
void TopicDesigner::setValues(const TopicEditorBase::TopicData &data, const QPoint &pos)
|
|
{
|
|
if (data.id == -1)
|
|
{
|
|
m_currentResizeHandle = HandleType::None;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
return;
|
|
}
|
|
|
|
if (m_resizeHandleActive != HandleType::None) // do not change anything while a resize is going on
|
|
return;
|
|
|
|
if (handleRect(data, HandleType::TopLeft).contains(pos))
|
|
{
|
|
m_currentResizeHandle = HandleType::TopLeft;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
return;
|
|
}
|
|
|
|
if (handleRect(data, HandleType::BottomRight).contains(pos))
|
|
{
|
|
m_currentResizeHandle = HandleType::BottomRight;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
return;
|
|
}
|
|
|
|
if (handleRect(data, HandleType::TopCenter).contains(pos))
|
|
{
|
|
m_currentResizeHandle = HandleType::TopCenter;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
return;
|
|
}
|
|
|
|
if (handleRect(data, HandleType::BottomCenter).contains(pos))
|
|
{
|
|
m_currentResizeHandle = HandleType::BottomCenter;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
return;
|
|
}
|
|
|
|
if (handleRect(data, HandleType::TopRight).contains(pos))
|
|
{
|
|
m_currentResizeHandle = HandleType::TopRight;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
return;
|
|
}
|
|
|
|
if (handleRect(data, HandleType::BottomLeft).contains(pos))
|
|
{
|
|
m_currentResizeHandle = HandleType::BottomLeft;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
return;
|
|
}
|
|
|
|
if (handleRect(data, HandleType::CenterLeft).contains(pos))
|
|
{
|
|
m_currentResizeHandle = HandleType::CenterLeft;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
return;
|
|
}
|
|
|
|
if (handleRect(data, HandleType::CenterRight).contains(pos))
|
|
{
|
|
m_currentResizeHandle = HandleType::CenterRight;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
return;
|
|
}
|
|
|
|
// if the user does not touch a handle, then the rect is touched directly
|
|
if (topicItemRect(data).contains(pos))
|
|
{
|
|
m_currentResizeHandle = HandleType::Whole;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
|
|
double x = pos.x();
|
|
x /= width();
|
|
double y = pos.y();
|
|
y /= height();
|
|
|
|
m_wholeOrgPos = QPointF(x, y);
|
|
|
|
//qCritical() << "org pos" << m_wholeOrgPos;
|
|
|
|
return;
|
|
}
|
|
|
|
// otherwise ..
|
|
m_currentResizeHandle = HandleType::None;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
}
|
|
|
|
|
|
void TopicDesigner::setCursorFromHandleType(const HandleType &ht)
|
|
{
|
|
switch (ht)
|
|
{
|
|
case HandleType::TopLeft:
|
|
case HandleType::BottomRight:
|
|
{
|
|
setCursor(QCursor(Qt::SizeFDiagCursor));
|
|
break;
|
|
}
|
|
|
|
case HandleType::TopCenter:
|
|
case HandleType::BottomCenter:
|
|
{
|
|
setCursor(QCursor(Qt::SizeVerCursor));
|
|
break;
|
|
}
|
|
|
|
case HandleType::TopRight:
|
|
case HandleType::BottomLeft:
|
|
{
|
|
setCursor(QCursor(Qt::SizeBDiagCursor));
|
|
break;
|
|
}
|
|
|
|
case HandleType::CenterLeft:
|
|
case HandleType::CenterRight:
|
|
{
|
|
setCursor(QCursor(Qt::SizeHorCursor));
|
|
break;
|
|
}
|
|
|
|
case HandleType::Whole:
|
|
{
|
|
setCursor(QCursor(Qt::SizeAllCursor));
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
setCursor(QCursor(Qt::ArrowCursor));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool TopicDesigner::checkConstraints(TopicEditorBase::TopicData &selected)
|
|
{
|
|
// if size of selected te on current action is too small => set to minimum size
|
|
|
|
bool result = true;
|
|
|
|
if (selected.x < 0)
|
|
{
|
|
// qCritical() << "affect: minimum value size constraint not met: x" << selected.x;
|
|
selected.x = 0;
|
|
result = false;
|
|
}
|
|
|
|
if (selected.y < 0)
|
|
{
|
|
// qCritical() << "affect: minimum value size constraint not met: y" << selected.y;
|
|
selected.y = 0;
|
|
result = false;
|
|
}
|
|
|
|
if (selected.w < minVal)
|
|
{
|
|
// qCritical() << "affect: minimum value size constraint not met: w" << selected.w;
|
|
selected.w = minVal;
|
|
result = false;
|
|
}
|
|
|
|
if (selected.h < minVal)
|
|
{
|
|
// qCritical() << "affect: minimum value size constraint not met: h" << selected.h;
|
|
selected.h = minVal;
|
|
result = false;
|
|
}
|
|
|
|
if (selected.x > maxVal)
|
|
{
|
|
// qCritical() << "affect: maximum value size constraint not met: x" << selected.x;
|
|
selected.x = maxVal;
|
|
result = false;
|
|
}
|
|
|
|
if (selected.y > maxVal)
|
|
{
|
|
// qCritical() << "affect: maximum value size constraint not met: y" << selected.y;
|
|
selected.y = maxVal;
|
|
result = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void TopicDesigner::affect(TopicEditorBase::TopicData &selected, const Affection &affection, const QPoint &pos)
|
|
{
|
|
double posX = pos.x();
|
|
posX /= width();
|
|
|
|
double posY = pos.y();
|
|
posY /= height();
|
|
|
|
// i guess wPos/hPos must always be bigger than x/y
|
|
|
|
switch (affection)
|
|
{
|
|
case Affection::x:
|
|
{
|
|
double x = selected.x;
|
|
double oldX = x;
|
|
x = posX;
|
|
selected.x = x;
|
|
|
|
double w = selected.w;
|
|
w += oldX - x;
|
|
selected.w = w;
|
|
|
|
break;
|
|
}
|
|
|
|
case Affection::y:
|
|
{
|
|
double y = selected.y;
|
|
double oldY = y;
|
|
y = posY;
|
|
selected.y = y;
|
|
|
|
double h = selected.h;
|
|
h += oldY - y;
|
|
selected.h = h;
|
|
|
|
break;
|
|
}
|
|
|
|
case Affection::w:
|
|
{
|
|
selected.w = posX - selected.x;
|
|
break;
|
|
}
|
|
|
|
case Affection::h:
|
|
{
|
|
selected.h = posY - selected.y;
|
|
break;
|
|
}
|
|
|
|
case Affection::Whole:
|
|
{
|
|
double x = selected.x;
|
|
double y = selected.y;
|
|
|
|
//qCritical() << "b4 whole x" << m_wholeOrgPos.x() << "te x" << x << "pos x" << posX;
|
|
|
|
x = -m_wholeOrgPos.x() + x + posX;
|
|
y = -m_wholeOrgPos.y() + y + posY;
|
|
|
|
m_wholeOrgPos = QPointF(posX, posY);
|
|
|
|
selected.x = x;
|
|
selected.y = y;
|
|
|
|
//qCritical() << "aftr whole x" << m_wholeOrgPos.x() << "te x" << x << "pos x" << posX;
|
|
}
|
|
}
|
|
|
|
// if (m_drawWidgetPreviewContent != nullptr)
|
|
// {
|
|
// m_drawWidgetPreviewContent->setGeometry(0, 0, width(), height());
|
|
// }
|
|
|
|
checkConstraints(selected);
|
|
emit changesMade();
|
|
}
|
|
|
|
|
|
void TopicDesigner::incIndex(const TopicEditorBase::TopicData &selected)
|
|
{
|
|
for (auto i = m_topicDataList.begin(); i != m_topicDataList.end(); ++i)
|
|
{
|
|
TopicEditorBase::TopicData data = *i;
|
|
|
|
if (data.id == selected.id)
|
|
{
|
|
auto next = (i + 1);
|
|
|
|
if (next == m_topicDataList.end())
|
|
{
|
|
//qCritical() << "is already last!";
|
|
return;
|
|
}
|
|
|
|
next = (i+2); // can be end or not
|
|
|
|
m_topicDataList.insert(next, data);
|
|
m_topicDataList.erase(i);
|
|
|
|
return; // !!
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void TopicDesigner::toFront(const TopicEditorBase::TopicData &selected)
|
|
{
|
|
auto begin = m_topicDataList.begin();
|
|
|
|
for (auto i = m_topicDataList.begin(); i != m_topicDataList.end(); ++i)
|
|
{
|
|
TopicEditorBase::TopicData data = *i;
|
|
|
|
if (data.id == selected.id)
|
|
{
|
|
m_topicDataList.insert(begin, data);
|
|
m_topicDataList.erase(i);
|
|
|
|
return; // !!
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void TopicDesigner::toBack(const TopicEditorBase::TopicData &selected)
|
|
{
|
|
auto end = m_topicDataList.begin();
|
|
|
|
for (auto i = m_topicDataList.begin(); i != m_topicDataList.end(); ++i)
|
|
{
|
|
TopicEditorBase::TopicData data = *i;
|
|
|
|
if (data.id == selected.id)
|
|
{
|
|
m_topicDataList.insert(end, data);
|
|
m_topicDataList.erase(i);
|
|
|
|
return; // !!
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void TopicDesigner::decIndex(const TopicEditorBase::TopicData &selected)
|
|
{
|
|
auto last = m_topicDataList.begin();
|
|
|
|
for (auto i = m_topicDataList.begin(); i != m_topicDataList.end(); ++i)
|
|
{
|
|
TopicEditorBase::TopicData data = *i;
|
|
|
|
if (data.id == selected.id)
|
|
{
|
|
m_topicDataList.insert(last, data);
|
|
m_topicDataList.erase(i);
|
|
|
|
return; // !!
|
|
}
|
|
|
|
last = i;
|
|
}
|
|
}
|
|
|
|
|
|
double TopicDesigner::oneWidthPixelInPercent()
|
|
{
|
|
double result = width();
|
|
return (1 / result);
|
|
}
|
|
|
|
|
|
double TopicDesigner::oneHeightPixelInPercent()
|
|
{
|
|
double result = height();
|
|
return (1 / result);
|
|
}
|
|
|
|
|
|
void TopicDesigner::keyPressEvent(QKeyEvent *event)
|
|
{
|
|
QWidget::keyPressEvent(event);
|
|
|
|
TopicEditorBase::TopicData selected;
|
|
|
|
if (m_selectedTopicId > -1) // TODO: to method !
|
|
{
|
|
// find selected
|
|
for (auto i = m_topicDataList.constBegin(); i != m_topicDataList.constEnd(); ++i)
|
|
{
|
|
TopicEditorBase::TopicData data = *i;
|
|
|
|
if (data.id == m_selectedTopicId)
|
|
{
|
|
selected = data;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selected.id == -1)
|
|
return;
|
|
|
|
|
|
double times = 5;
|
|
if (event->modifiers().testFlag(Qt::ControlModifier))
|
|
{
|
|
times = 1;
|
|
}
|
|
|
|
bool shapeShift = false;
|
|
if (event->modifiers().testFlag(Qt::ShiftModifier))
|
|
{
|
|
shapeShift = true;
|
|
}
|
|
|
|
|
|
switch (event->key()) // TODO: process multiple keys being pressed
|
|
{
|
|
// case Qt::Key_Minus:
|
|
// {
|
|
// decIndex(selected);
|
|
// break;
|
|
// }
|
|
|
|
// case Qt::Key_Plus:
|
|
// {
|
|
// incIndex(selected);
|
|
// break;
|
|
// }
|
|
|
|
case Qt::Key_Up:
|
|
{
|
|
double moveBy = oneHeightPixelInPercent() * times;
|
|
|
|
if (shapeShift)
|
|
selected.h -= moveBy;
|
|
else
|
|
selected.y -= moveBy;
|
|
|
|
break;
|
|
}
|
|
|
|
case Qt::Key_Down:
|
|
{
|
|
double moveBy = oneHeightPixelInPercent() * times;
|
|
|
|
if (shapeShift)
|
|
selected.h += moveBy;
|
|
else
|
|
selected.y += moveBy;
|
|
|
|
break;
|
|
}
|
|
|
|
case Qt::Key_Left:
|
|
{
|
|
double moveBy = oneHeightPixelInPercent() * times;
|
|
|
|
if (shapeShift)
|
|
selected.w -= moveBy;
|
|
else
|
|
selected.x -= moveBy;
|
|
|
|
break;
|
|
}
|
|
|
|
case Qt::Key_Right:
|
|
{
|
|
double moveBy = oneHeightPixelInPercent() * times;
|
|
|
|
if (shapeShift)
|
|
selected.w += moveBy;
|
|
else
|
|
selected.x += moveBy;
|
|
|
|
break;
|
|
}
|
|
|
|
case Qt::Key_Escape:
|
|
{
|
|
unselect();
|
|
|
|
break;
|
|
}
|
|
|
|
// case Qt::Key_Delete:
|
|
// {
|
|
// deleteTrackEntry(selected);
|
|
|
|
// break;
|
|
// }
|
|
}
|
|
|
|
checkConstraints(selected);
|
|
|
|
update();
|
|
|
|
emit changesMade();
|
|
}
|
|
|
|
|
|
void TopicDesigner::deleteTopicItem(const TopicEditorBase::TopicData &data)
|
|
{
|
|
// find selected
|
|
for (auto i = m_topicDataList.begin(); i != m_topicDataList.end(); ++i)
|
|
{
|
|
TopicEditorBase::TopicData other = *i;
|
|
if (other.id == data.id)
|
|
{
|
|
m_topicDataList.erase(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_currentResizeHandle = HandleType::None;
|
|
setCursorFromHandleType(m_currentResizeHandle);
|
|
|
|
update();
|
|
|
|
emit deleteTopicItem(data.id);
|
|
emit changesMade();
|
|
}
|
|
|
|
|
|
TopicEditorBase::TopicData &TopicDesigner::selectedTopicData() noexcept(false)
|
|
{
|
|
if (m_selectedTopicId == -1)
|
|
throw QStringLiteral("getSelectedTopic: no selected topic");
|
|
|
|
// find selected
|
|
for (auto it = m_topicDataList.begin(); it != m_topicDataList.end(); ++it)
|
|
{
|
|
const TopicEditorBase::TopicData &data = *it;
|
|
if (data.id == m_selectedTopicId)
|
|
return *it;
|
|
}
|
|
|
|
throw QStringLiteral("getSelectedTopic: data not found for selected topic with id %1").arg(m_selectedTopicId);
|
|
}
|
|
|
|
void TopicDesigner::setTopicData(const TopicEditorBase::TopicData &data)
|
|
{
|
|
auto before = m_topicDataList.begin();
|
|
for (auto it = m_topicDataList.begin(); it != m_topicDataList.end(); ++it)
|
|
{
|
|
if (it->id == data.id)
|
|
{
|
|
m_topicDataList.insert(before, data);
|
|
m_topicDataList.erase(it);
|
|
return;
|
|
}
|
|
before = it;
|
|
}
|
|
}
|
|
|
|
|
|
void TopicDesigner::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
QWidget::mouseMoveEvent(event);
|
|
try
|
|
{
|
|
TopicEditorBase::TopicData &selected = selectedTopicData();
|
|
setValues(selected, mapFromGlobal(event->globalPos()));
|
|
|
|
if (selected.id == -1)
|
|
return;
|
|
|
|
if (m_resizeHandleActive == HandleType::None)
|
|
return;
|
|
|
|
QPoint pos = mapFromGlobal(event->globalPos());
|
|
switch (m_resizeHandleActive)
|
|
{
|
|
case HandleType::TopLeft: // x & y
|
|
{
|
|
affect(selected, Affection::x, pos);
|
|
affect(selected, Affection::y, pos);
|
|
break;
|
|
}
|
|
|
|
case HandleType::TopCenter: // y
|
|
{
|
|
affect(selected, Affection::y, pos);
|
|
break;
|
|
}
|
|
|
|
case HandleType::TopRight: // w & y
|
|
{
|
|
affect(selected, Affection::w, pos);
|
|
affect(selected, Affection::y, pos);
|
|
break;
|
|
}
|
|
|
|
|
|
case HandleType::CenterLeft: // x
|
|
{
|
|
affect(selected, Affection::x, pos);
|
|
break;
|
|
}
|
|
|
|
case HandleType::CenterRight: // w
|
|
{
|
|
affect(selected, Affection::w, pos);
|
|
break;
|
|
}
|
|
|
|
|
|
case HandleType::BottomLeft: // x & h
|
|
{
|
|
affect(selected, Affection::x, pos);
|
|
affect(selected, Affection::h, pos);
|
|
break;
|
|
}
|
|
|
|
case HandleType::BottomCenter: // h
|
|
{
|
|
affect(selected, Affection::h, pos);
|
|
break;
|
|
}
|
|
|
|
case HandleType::BottomRight: // w & h
|
|
{
|
|
affect(selected, Affection::w, pos);
|
|
affect(selected, Affection::h, pos);
|
|
break;
|
|
}
|
|
|
|
case HandleType::Whole:
|
|
{
|
|
affect(selected, Affection::Whole, pos);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
qCritical() << "cannot affect this!";
|
|
break;
|
|
}
|
|
} // switch
|
|
|
|
update();
|
|
emit changesMade();
|
|
}
|
|
catch (const QString &errMsg) { qCritical() << errMsg; }
|
|
}
|
|
|
|
|
|
void TopicDesigner::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
QWidget::mousePressEvent(event);
|
|
|
|
if (m_currentResizeHandle == HandleType::None)
|
|
return;
|
|
|
|
m_resizeHandleActive = m_currentResizeHandle;
|
|
update();
|
|
|
|
//emit signalChangesMade();
|
|
}
|
|
|
|
|
|
void TopicDesigner::mouseReleaseEvent(QMouseEvent *event)
|
|
{
|
|
QWidget::mouseReleaseEvent(event);
|
|
|
|
if (m_resizeHandleActive == HandleType::None)
|
|
{
|
|
unselect();
|
|
findSelection(mapFromGlobal(event->globalPos()));
|
|
}
|
|
|
|
m_resizeHandleActive = HandleType::None;
|
|
|
|
update();
|
|
|
|
emit changesMade();
|
|
}
|
|
|
|
|
|
void TopicDesigner::internalSelectTopic(int topicId)
|
|
{
|
|
if (topicId < 0)
|
|
unselect();
|
|
|
|
m_selectedTopicId = topicId;
|
|
emit topicSelected(m_selectedTopicId);
|
|
}
|
|
|
|
|
|
void TopicDesigner::findSelection(const QPoint &pos)
|
|
{
|
|
for (auto i = m_topicDataList.crbegin(); i != m_topicDataList.crend(); ++i) // stl its (reverse order)!
|
|
{
|
|
TopicEditorBase::TopicData data = *i;
|
|
|
|
if (topicItemRect(data).contains(pos))
|
|
{
|
|
// found!
|
|
internalSelectTopic(data.id);
|
|
update();
|
|
//emit signalChangesMade();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void TopicDesigner::unselect()
|
|
{
|
|
m_resizeHandleActive = HandleType::None;
|
|
m_selectedTopicId = -1;
|
|
emit unselected();
|
|
}
|
|
|
|
|
|
QRect TopicDesigner::handleRect(const TopicEditorBase::TopicData &data, const HandleType &ht)
|
|
{
|
|
QRect teRect = topicItemRect(data);
|
|
|
|
int x = teRect.x();
|
|
int y = teRect.y();
|
|
int w = teRect.width();
|
|
int h = teRect.height();
|
|
|
|
int halfHandleSize = handleSize / 2;
|
|
QSize handleSized(handleSize, handleSize);
|
|
|
|
int leftX = x - halfHandleSize + 1;
|
|
int centerX = x + (w / 2) - halfHandleSize;
|
|
int rightX = x + w - halfHandleSize - 1;
|
|
|
|
int topY = y - halfHandleSize + 1;
|
|
int centerY = y + (h / 2) - halfHandleSize;
|
|
int bottomY = y + h - halfHandleSize - 1;
|
|
|
|
switch (ht)
|
|
{
|
|
case HandleType::TopLeft:
|
|
return QRect(QPoint(leftX, topY), handleSized);
|
|
case HandleType::TopCenter:
|
|
return QRect(QPoint(centerX, topY), handleSized);
|
|
case HandleType::TopRight:
|
|
return QRect(QPoint(rightX, topY), handleSized);
|
|
|
|
case HandleType::CenterLeft:
|
|
return QRect(QPoint(leftX, centerY), handleSized);
|
|
case HandleType::CenterRight:
|
|
return QRect(QPoint(rightX, centerY), handleSized);
|
|
|
|
case HandleType::BottomLeft:
|
|
return QRect(QPoint(leftX, bottomY), handleSized);
|
|
case HandleType::BottomCenter:
|
|
return QRect(QPoint(centerX, bottomY), handleSized);
|
|
case HandleType::BottomRight:
|
|
return QRect(QPoint(rightX, bottomY), handleSized);
|
|
default:
|
|
return QRect();
|
|
}
|
|
}
|
|
|
|
|
|
QRect TopicDesigner::topicItemRect(const TopicEditorBase::TopicData &data)
|
|
{
|
|
// const double minVal = 0.01f;
|
|
|
|
if (data.x < 0 || data.y < 0 || data.w < minVal || data.h < minVal)
|
|
{
|
|
qCritical() << "trackEntryRect failed: x" << data.x << "y" << data.y << "w" << data.w << "h" << data.h;
|
|
return QRect();
|
|
}
|
|
|
|
//qCritical() << "te geo % : x" << te.x << "y" << te.y << "w" << te.w << "h" << te.h;
|
|
|
|
int x = width() * data.x;
|
|
int y = height() * data.y;
|
|
int w = width() * data.w;
|
|
int h = height() * data.h;
|
|
|
|
//qCritical() << "te geo px: x" << x << "y" << y << "w" << w << "h" << h;
|
|
|
|
return QRect(x-4, y-4, w, h);
|
|
}
|
|
|
|
|
|
void TopicDesigner::paintSelectedTopicItem(QPainter *p, const TopicEditorBase::TopicData &data)
|
|
{
|
|
p->save();
|
|
|
|
// draw handles
|
|
p->setBrush(QColor("white"));
|
|
p->setPen(QPen(QColor("black")));
|
|
|
|
// top left
|
|
p->drawRect(handleRect(data, HandleType::TopLeft));
|
|
|
|
// top center
|
|
p->drawRect(handleRect(data, HandleType::TopCenter));
|
|
|
|
// top right
|
|
p->drawRect(handleRect(data, HandleType::TopRight));
|
|
|
|
// center left
|
|
p->drawRect(handleRect(data, HandleType::CenterLeft));
|
|
|
|
// center right
|
|
p->drawRect(handleRect(data, HandleType::CenterRight));
|
|
|
|
// bottom left
|
|
p->drawRect(handleRect(data, HandleType::BottomLeft));
|
|
|
|
// bottom center
|
|
p->drawRect(handleRect(data, HandleType::BottomCenter));
|
|
|
|
// bottom right
|
|
p->drawRect(handleRect(data, HandleType::BottomRight));
|
|
|
|
p->restore();
|
|
}
|
|
|
|
|
|
void TopicDesigner::paintEvent(QPaintEvent *)
|
|
{
|
|
//QWidget::paintEvent(event);
|
|
|
|
QPainter painter(this);
|
|
painter.save();
|
|
painter.setPen(QPen(QColor("black"), 1));
|
|
//painter.setBrush(QColor("white"));
|
|
painter.drawRect(0, 0, width()-1, height()-1);
|
|
painter.restore();
|
|
|
|
if (m_topicDataList.isEmpty())
|
|
return;
|
|
|
|
for (auto i = m_topicDataList.constBegin(); i != m_topicDataList.constEnd(); ++i)
|
|
{
|
|
TopicEditorBase::TopicData data = *i;
|
|
|
|
//paintTrackEntry(te);
|
|
|
|
painter.save();
|
|
|
|
if (data.id == m_selectedTopicId)
|
|
{
|
|
painter.setBrush(QColor("red"));
|
|
painter.setPen(QPen(QColor("blue"), 2));
|
|
}
|
|
else
|
|
{
|
|
painter.setBrush(QColor("white"));
|
|
painter.setPen(QPen(QColor("black"), 2));
|
|
}
|
|
|
|
painter.drawRect(topicItemRect(data));
|
|
painter.restore();
|
|
}
|
|
|
|
if (m_selectedTopicId > -1 || m_drawWidgetPreviewContent != nullptr) // draw if either the topic is selected or if a preview widget is available (both are different modes)
|
|
{
|
|
// find selected
|
|
for (auto i = m_topicDataList.constBegin(); i != m_topicDataList.constEnd(); ++i)
|
|
{
|
|
TopicEditorBase::TopicData data = *i;
|
|
|
|
if (data.id == m_selectedTopicId || m_drawWidgetPreviewContent != nullptr)
|
|
{
|
|
if (m_drawWidgetPreviewContent != nullptr)
|
|
{
|
|
//qWarning() << "te x" << data.x << "y" << data.y << "w" << data.w << "h" << data.h;
|
|
m_drawWidgetPreviewContent->setGeometry(0, 0, (width() * data.w) - 4, (height() * data.h) - 4);
|
|
painter.drawPixmap((width() * data.x) - 2, (height() * data.y) - 2, (width() * data.w) - 4 , (height() * data.h) - 4, m_drawWidgetPreviewContent->grab());
|
|
}
|
|
|
|
paintSelectedTopicItem(&painter, data);
|
|
//qCritical() << "drew selected track entry";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace PresentationManagement
|