2018-12-22 21:03:51 -05:00
# include "nodeobject.h"
using Xybrid : : UI : : NodeObject ;
using Xybrid : : UI : : PortObject ;
using Xybrid : : UI : : PortConnectionObject ;
using Xybrid : : Data : : Node ;
using Xybrid : : Data : : Port ;
# include <cmath>
# include <QDebug>
# include <QTimer>
# include <QPainter>
# include <QGraphicsScene>
# include <QStyleOptionGraphicsItem>
# include <QGraphicsSceneHoverEvent>
# include <QTextDocument>
# include <QTextCharFormat>
# include <QTextCursor>
# include <QMenu>
# include <QMessageBox>
2018-12-25 01:54:23 -05:00
# include <QInputDialog>
2018-12-22 21:03:51 -05:00
2018-12-25 01:54:23 -05:00
# include "util/strings.h"
2018-12-22 21:03:51 -05:00
2018-12-25 01:54:23 -05:00
namespace {
2018-12-22 21:03:51 -05:00
const QColor tcolor [ ] {
QColor ( 239 , 179 , 59 ) , // Audio
QColor ( 163 , 95 , 191 ) , // Command
QColor ( 95 , 191 , 163 ) , // MIDI
QColor ( 127 , 127 , 255 ) , // Parameter
} ;
}
2018-12-28 12:19:32 -05:00
void PortObject : : connectTo ( PortObject * o ) {
2018-12-22 21:03:51 -05:00
if ( ! o ) return ;
if ( connections . find ( o ) ! = connections . end ( ) ) return ;
if ( port - > type = = o - > port - > type ) return ;
PortObject * in ;
PortObject * out ;
if ( port - > type = = Port : : Input ) { in = this ; out = o ; }
else { out = this ; in = o ; }
if ( out - > port - > connect ( in - > port ) ) {
/*auto* pc =*/ new PortConnectionObject ( in , out ) ;
}
}
void PortObject : : setHighlighted ( bool h , bool hideLabel ) {
highlighted = h ;
bool lv = h & & ! hideLabel ;
if ( lv ) {
2018-12-25 01:54:23 -05:00
QString txt = QString ( " %1 %2 " ) . arg ( Util : : enumName ( port - > dataType ( ) ) . toLower ( ) ) . arg ( Util : : hex ( port - > index ) ) ;
2018-12-22 21:03:51 -05:00
if ( ! port - > name . empty ( ) ) txt = QString ( " %1 (%2) " ) . arg ( QString : : fromStdString ( port - > name ) ) . arg ( txt ) ;
QColor c = tcolor [ port - > dataType ( ) ] ;
label - > setText ( txt ) ;
label - > setBrush ( c ) ;
labelShadow - > setText ( txt ) ;
labelShadow - > setBrush ( c . darker ( 400 ) ) ;
labelShadow - > setPen ( QPen ( labelShadow - > brush ( ) , 2.5 ) ) ;
auto lbr = label - > boundingRect ( ) ;
if ( port - > type = = Port : : Input ) label - > setPos ( QPointF ( - lbr . width ( ) - ( portSize / 2 + portSpacing ) , lbr . height ( ) * - .5 ) ) ;
else label - > setPos ( QPointF ( portSize / 2 + portSpacing , lbr . height ( ) * - .5 ) ) ;
auto lbsr = labelShadow - > boundingRect ( ) ;
labelShadow - > setPos ( label - > pos ( ) + ( lbr . bottomRight ( ) - lbsr . bottomRight ( ) ) / 2 ) ;
}
label - > setVisible ( lv ) ;
labelShadow - > setVisible ( lv ) ;
update ( ) ;
}
PortObject : : PortObject ( const std : : shared_ptr < Data : : Port > & p ) {
port = p ;
p - > obj = this ;
setAcceptHoverEvents ( true ) ;
setAcceptedMouseButtons ( Qt : : LeftButton ) ;
setFlag ( QGraphicsItem : : ItemSendsScenePositionChanges ) ;
labelShadow = new QGraphicsSimpleTextItem ( this ) ;
labelShadow - > setVisible ( false ) ;
label = new QGraphicsSimpleTextItem ( this ) ;
label - > setVisible ( false ) ;
for ( auto c : port - > connections ) {
if ( auto cc = c . lock ( ) ; cc & & cc - > obj ) connectTo ( cc - > obj ) ;
}
2018-12-25 01:54:23 -05:00
setCursor ( Qt : : CursorShape : : CrossCursor ) ;
2018-12-22 21:03:51 -05:00
}
PortObject : : ~ PortObject ( ) {
while ( connections . begin ( ) ! = connections . end ( ) ) delete connections . begin ( ) - > second ;
}
void PortObject : : mousePressEvent ( QGraphicsSceneMouseEvent * ) {
2018-12-25 01:54:23 -05:00
//setCursor(Qt::CursorShape::CrossCursor);
2018-12-22 21:03:51 -05:00
setHighlighted ( true , true ) ;
dragLine . reset ( new QGraphicsLineItem ( ) ) ;
dragLine - > setPen ( QPen ( tcolor [ port - > dataType ( ) ] . lighter ( 125 ) , 1.5 ) ) ;
dragLine - > setLine ( QLineF ( scenePos ( ) , scenePos ( ) ) ) ;
dragLine - > setZValue ( 100 ) ;
scene ( ) - > addItem ( dragLine . get ( ) ) ;
}
void PortObject : : mouseReleaseEvent ( QGraphicsSceneMouseEvent * e ) {
2018-12-25 01:54:23 -05:00
//unsetCursor();
2018-12-22 21:03:51 -05:00
dragLine . reset ( ) ;
auto * i = scene ( ) - > itemAt ( e - > scenePos ( ) , QTransform ( ) ) ;
if ( i & & i - > type ( ) = = PortObject : : Type ) {
auto * p = static_cast < PortObject * > ( i ) ;
//qDebug() << "connection:" << port->connect(p->port);
connectTo ( p ) ;
}
}
void PortObject : : mouseMoveEvent ( QGraphicsSceneMouseEvent * e ) {
if ( dragLine ) dragLine - > setLine ( QLineF ( scenePos ( ) , e - > scenePos ( ) ) ) ;
update ( ) ;
}
void PortObject : : hoverEnterEvent ( QGraphicsSceneHoverEvent * ) {
setHighlighted ( true ) ;
}
void PortObject : : hoverLeaveEvent ( QGraphicsSceneHoverEvent * ) {
setHighlighted ( false ) ;
}
void PortObject : : contextMenuEvent ( QGraphicsSceneContextMenuEvent * e ) {
auto * m = new QMenu ( ) ;
m - > addAction ( " Disconnect All " , this , [ this ] {
while ( connections . begin ( ) ! = connections . end ( ) ) {
auto * c = connections . begin ( ) - > second ;
c - > in - > port - > disconnect ( c - > out - > port ) ;
delete c ;
}
} ) ; //->setEnabled(this->connections.size() != 0);
2018-12-25 01:54:23 -05:00
m - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
2018-12-22 21:03:51 -05:00
m - > popup ( e - > screenPos ( ) ) ;
}
void PortObject : : paint ( QPainter * painter , const QStyleOptionGraphicsItem * , QWidget * ) {
QColor bg = tcolor [ port - > dataType ( ) ] ;
QColor outline = bg . darker ( 200 ) ;
if ( highlighted ) outline = bg . lighter ( 150 ) ;
painter - > setRenderHint ( QPainter : : RenderHint : : Antialiasing ) ;
painter - > setBrush ( QBrush ( bg ) ) ;
painter - > setPen ( QPen ( QBrush ( outline ) , 1 ) ) ;
painter - > drawEllipse ( boundingRect ( ) ) ;
}
QRectF PortObject : : boundingRect ( ) const {
return QRectF ( portSize * - .5 , portSize * - .5 , portSize , portSize ) ;
}
NodeObject : : NodeObject ( const std : : shared_ptr < Data : : Node > & n ) {
node = n ;
2018-12-25 01:54:23 -05:00
node - > obj = this ;
2018-12-22 21:03:51 -05:00
setFlag ( QGraphicsItem : : ItemIsMovable ) ;
setFlag ( QGraphicsItem : : ItemIsFocusable ) ;
setFlag ( QGraphicsItem : : ItemIsSelectable ) ;
setFlag ( QGraphicsItem : : ItemSendsScenePositionChanges ) ;
setPos ( node - > x , node - > y ) ;
connect ( this , & QGraphicsObject : : xChanged , this , & NodeObject : : onMoved ) ;
connect ( this , & QGraphicsObject : : yChanged , this , & NodeObject : : onMoved ) ;
2018-12-25 01:54:23 -05:00
//setToolTip(QString::fromStdString(node->name));
2018-12-22 21:03:51 -05:00
createPorts ( ) ;
2018-12-25 01:54:23 -05:00
node - > onGadgetCreated ( ) ;
}
void NodeObject : : setGadgetSize ( QPointF p ) {
gadgetSize_ = p ;
updateGeometry ( ) ;
2018-12-22 21:03:51 -05:00
}
void NodeObject : : promptDelete ( ) {
QPointer < NodeObject > t = this ;
if ( QMessageBox : : warning ( nullptr , " Are you sure? " , QString ( " Remove node? (This cannot be undone!) " ) , QMessageBox : : Yes | QMessageBox : : No , QMessageBox : : No ) ! = QMessageBox : : Yes ) return ;
if ( t ) {
t - > node - > parentTo ( nullptr ) ; // unparent node so it gets deleted
delete t ;
}
}
2018-12-25 01:54:23 -05:00
void NodeObject : : mouseDoubleClickEvent ( QGraphicsSceneMouseEvent * ) {
node - > onDoubleClick ( ) ;
}
2018-12-22 21:03:51 -05:00
void NodeObject : : contextMenuEvent ( QGraphicsSceneContextMenuEvent * e ) {
2018-12-25 01:54:23 -05:00
if ( ! isSelected ( ) ) {
for ( auto * s : scene ( ) - > selectedItems ( ) ) s - > setSelected ( false ) ;
setSelected ( true ) ;
}
2018-12-22 21:03:51 -05:00
auto * m = new QMenu ( ) ;
2018-12-25 01:54:23 -05:00
if ( canRename ) {
m - > addAction ( " Rename... " , this , [ this ] {
bool ok = false ;
auto cn = node - > name . empty ( ) ? QString : : fromStdString ( node - > pluginName ( ) ) : QString ( " \" %1 \" " ) . arg ( QString : : fromStdString ( node - > name ) ) ;
auto capt = QString ( " Rename %1: " ) . arg ( cn ) ;
auto n = QInputDialog : : getText ( nullptr , " Rename... " , capt , QLineEdit : : Normal , QString : : fromStdString ( node - > name ) , & ok ) ;
if ( ! ok ) return ; // canceled
//setToolTip(n);
node - > name = n . toStdString ( ) ;
node - > onRename ( ) ;
} ) ;
}
2018-12-22 21:03:51 -05:00
m - > addAction ( " Delete node " , this , & NodeObject : : promptDelete ) ;
2018-12-25 01:54:23 -05:00
m - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
2018-12-22 21:03:51 -05:00
m - > popup ( e - > screenPos ( ) ) ;
}
void NodeObject : : bringToTop ( bool force ) {
for ( auto o : collidingItems ( ) ) if ( o - > parentItem ( ) = = parentItem ( ) & & ( force | | ! o - > isSelected ( ) ) ) o - > stackBefore ( this ) ;
}
void NodeObject : : createPorts ( ) {
2018-12-25 01:54:23 -05:00
auto * ipc = new QGraphicsLineItem ( this ) ;
auto * opc = new QGraphicsLineItem ( this ) ;
inputPortContainer . reset ( ipc ) ;
outputPortContainer . reset ( opc ) ;
2018-12-22 21:03:51 -05:00
updateGeometry ( ) ;
2018-12-25 01:54:23 -05:00
QPen p ( QColor ( 95 , 95 , 95 ) , 2.5 ) ;
QPointF inc ( 0 , PortObject : : portSize + PortObject : : portSpacing ) ;
2018-12-22 21:03:51 -05:00
QPointF cursor = QPointF ( 0 , 0 ) ;
for ( auto mdt : node - > inputs ) {
for ( auto pp : mdt . second ) {
auto * p = new PortObject ( pp . second ) ;
2018-12-25 01:54:23 -05:00
p - > setParentItem ( ipc ) ;
2018-12-22 21:03:51 -05:00
p - > setPos ( cursor ) ;
2018-12-25 01:54:23 -05:00
cursor + = inc ;
2018-12-22 21:03:51 -05:00
}
}
2018-12-25 01:54:23 -05:00
ipc - > setVisible ( cursor . y ( ) > 0 ) ;
cursor - = inc ;
ipc - > setLine ( QLineF ( QPointF ( 0 , 0 ) , cursor ) ) ;
ipc - > setPen ( p ) ;
2018-12-22 21:03:51 -05:00
cursor = QPointF ( 0 , 0 ) ;
for ( auto mdt : node - > outputs ) {
for ( auto pp : mdt . second ) {
auto * p = new PortObject ( pp . second ) ;
2018-12-25 01:54:23 -05:00
p - > setParentItem ( opc ) ;
2018-12-22 21:03:51 -05:00
p - > setPos ( cursor ) ;
2018-12-25 01:54:23 -05:00
cursor + = inc ;
2018-12-22 21:03:51 -05:00
}
}
2018-12-25 01:54:23 -05:00
opc - > setVisible ( cursor . y ( ) > 0 ) ;
cursor - = inc ;
opc - > setLine ( QLineF ( QPointF ( 0 , 0 ) , cursor ) ) ;
opc - > setPen ( p ) ;
2018-12-22 21:03:51 -05:00
}
void NodeObject : : updateGeometry ( ) {
2018-12-25 01:54:23 -05:00
qreal pm = PortObject : : portSize * .5 + PortObject : : portSpacing ;
if ( inputPortContainer ) inputPortContainer - > setPos ( QPointF ( - pm , PortObject : : portSize ) ) ;
if ( outputPortContainer ) outputPortContainer - > setPos ( QPointF ( boundingRect ( ) . width ( ) + pm , PortObject : : portSize ) ) ;
2018-12-22 21:03:51 -05:00
}
void NodeObject : : onMoved ( ) {
if ( x ( ) < 0 ) setX ( 0 ) ;
else setX ( std : : round ( x ( ) ) ) ;
if ( y ( ) < 0 ) setY ( 0 ) ;
else setY ( std : : round ( y ( ) ) ) ;
node - > x = static_cast < int > ( x ( ) ) ;
node - > y = static_cast < int > ( y ( ) ) ;
if ( isSelected ( ) ) {
bringToTop ( ) ;
}
if ( auto s = scene ( ) ; s ) s - > update ( ) ;
}
void NodeObject : : focusInEvent ( QFocusEvent * ) {
bringToTop ( true ) ;
}
void NodeObject : : paint ( QPainter * painter , const QStyleOptionGraphicsItem * opt , QWidget * ) {
2018-12-25 01:54:23 -05:00
if ( customChrome ) {
node - > drawCustomChrome ( painter , opt ) ;
return ;
}
2018-12-22 21:03:51 -05:00
QRectF r = boundingRect ( ) ;
QColor outline = QColor ( 31 , 31 , 31 ) ;
if ( opt - > state & QStyle : : State_Selected ) outline = QColor ( 127 , 127 , 255 ) ;
QLinearGradient fill ( QPointF ( 0 , 0 ) , QPointF ( 0 , r . height ( ) ) ) ;
fill . setColorAt ( 0 , QColor ( 95 , 95 , 95 ) ) ;
fill . setColorAt ( 16.0 / r . height ( ) , QColor ( 63 , 63 , 63 ) ) ;
fill . setColorAt ( 1.0 - ( 1.0 - 16.0 / r . height ( ) ) / 2 , QColor ( 55 , 55 , 55 ) ) ;
fill . setColorAt ( 1 , QColor ( 35 , 35 , 35 ) ) ;
painter - > setRenderHint ( QPainter : : RenderHint : : Antialiasing ) ;
painter - > setBrush ( QBrush ( fill ) ) ;
painter - > setPen ( QPen ( QBrush ( outline ) , 2 ) ) ;
painter - > drawRoundedRect ( r , 8 , 8 ) ;
QRectF tr = r - QMargins ( 3 , 2 , 3 , 0 ) ;
if ( ! node - > name . empty ( ) ) {
painter - > setPen ( QColor ( 222 , 222 , 222 ) ) ;
painter - > drawText ( tr , Qt : : AlignLeft , QString : : fromStdString ( node - > name ) ) ;
tr - = QMarginsF ( 0 , painter - > fontMetrics ( ) . height ( ) , 0 , 0 ) ;
}
painter - > setPen ( QColor ( 171 , 171 , 171 ) ) ;
painter - > drawText ( tr , Qt : : AlignLeft , QString : : fromStdString ( node - > pluginName ( ) ) ) ;
}
QRectF NodeObject : : boundingRect ( ) const {
2018-12-25 01:54:23 -05:00
if ( customChrome ) return QRectF ( QPointF ( 0 , 0 ) , gadgetSize_ ) ;
return QRectF ( 0 , 0 , 192 , 36 ) ; // + QMarginsF(8, 8, 8, 8);
2018-12-22 21:03:51 -05:00
}
PortConnectionObject : : PortConnectionObject ( PortObject * in , PortObject * out ) {
this - > in = in ;
this - > out = out ;
2018-12-28 12:19:32 -05:00
// remove dupes
if ( in - > connections [ out ] ) delete in - > connections [ out ] ;
if ( out - > connections [ in ] ) delete out - > connections [ in ] ;
// and hook up
2018-12-22 21:03:51 -05:00
in - > connections [ out ] = this ;
out - > connections [ in ] = this ;
QTimer : : singleShot ( 1 , [ this ] { this - > in - > scene ( ) - > addItem ( this ) ; } ) ;
setZValue ( - 100 ) ;
setAcceptHoverEvents ( true ) ;
//setFlag(QGraphicsItem::GraphicsItemFlag::)
QTimer : : singleShot ( 1 , [ this ] {
auto * op = static_cast < QGraphicsObject * > ( this - > out - > parentItem ( ) - > parentItem ( ) ) ;
auto * ip = static_cast < QGraphicsObject * > ( this - > in - > parentItem ( ) - > parentItem ( ) ) ;
connect ( op , & QGraphicsObject : : xChanged , this , & PortConnectionObject : : updateEnds ) ;
connect ( op , & QGraphicsObject : : yChanged , this , & PortConnectionObject : : updateEnds ) ;
connect ( ip , & QGraphicsObject : : xChanged , this , & PortConnectionObject : : updateEnds ) ;
connect ( ip , & QGraphicsObject : : xChanged , this , & PortConnectionObject : : updateEnds ) ;
updateEnds ( ) ;
} ) ;
}
void PortConnectionObject : : updateEnds ( ) {
setPos ( ( out - > scenePos ( ) + in - > scenePos ( ) ) * .5 ) ;
update ( ) ;
}
PortConnectionObject : : ~ PortConnectionObject ( ) {
in - > connections . erase ( out ) ;
out - > connections . erase ( in ) ;
}
void PortConnectionObject : : disconnect ( ) {
out - > port - > disconnect ( in - > port ) ;
delete this ;
}
void PortConnectionObject : : hoverEnterEvent ( QGraphicsSceneHoverEvent * ) {
highlighted = true ;
update ( ) ;
}
void PortConnectionObject : : hoverLeaveEvent ( QGraphicsSceneHoverEvent * ) {
highlighted = false ;
update ( ) ;
}
void PortConnectionObject : : contextMenuEvent ( QGraphicsSceneContextMenuEvent * e ) {
auto * m = new QMenu ( ) ;
m - > addAction ( " Disconnect " , this , [ this ] {
disconnect ( ) ;
} ) ;
m - > popup ( e - > screenPos ( ) ) ;
}
void PortConnectionObject : : paint ( QPainter * painter , const QStyleOptionGraphicsItem * , QWidget * ) {
QColor c = tcolor [ in - > port - > dataType ( ) ] ;
if ( highlighted ) c = c . lighter ( 150 ) ;
painter - > setPen ( Qt : : NoPen ) ;
painter - > setBrush ( QBrush ( c ) ) ;
painter - > drawPath ( shape ( 2.5 ) ) ;
}
QRectF PortConnectionObject : : boundingRect ( ) const {
return shape ( ) . boundingRect ( ) . normalized ( ) ; //controlPointRect().normalized().united(QRectF(mapFromScene(out->scenePos()), mapFromScene(in->scenePos())));
}
QPainterPath PortConnectionObject : : shape ( qreal width ) const {
QPainterPath path ;
auto start = mapFromScene ( out - > scenePos ( ) ) ;
auto end = mapFromScene ( in - > scenePos ( ) ) ;
path . moveTo ( start ) ;
//QPointF mod(std::max(std::max((end.x() - start.x()) * .64, (start.x() - end.x()) * .24), 64.0), 0);
2018-12-25 01:54:23 -05:00
QPointF out ( 0 , 0 ) ;
//path.lineTo(start+out);
2018-12-22 21:03:51 -05:00
QPointF mod ( std : : max ( ( end . x ( ) - start . x ( ) ) * .64 , 96.0 ) , 0 ) ;
2018-12-25 01:54:23 -05:00
path . cubicTo ( start + out + mod , end - mod , end - out ) ;
//path.lineTo(end);
2018-12-22 21:03:51 -05:00
if ( width < = 0 ) return path ;
QPainterPathStroker qp ;
qp . setWidth ( width ) ;
qp . setCapStyle ( Qt : : PenCapStyle : : RoundCap ) ;
qp . setJoinStyle ( Qt : : PenJoinStyle : : RoundJoin ) ;
auto p = qp . createStroke ( path ) ;
return p ;
}