Skip to content
This repository
Browse code

qtcollider: refactor drag-and-drop for robustness

* only ever use SC handlers if drag originated in SC.

* use all SC handlers or none, avoid mixing them with those of
  the widget implementation

* instead of passing data of external drags to SC, let the widget
  implementation handle it

* try to store data of drags originating in SC in the Qt drag data
  object, thus making it available to the widget implementation,
  as well as other applications
  • Loading branch information...
commit 503eeb9241dd58e5355f5d824f9213e94143a0dc 1 parent dc2aeff
Jakob Leben jleben authored
38 QtCollider/QWidgetProxy.cpp
@@ -214,8 +214,8 @@ void QWidgetProxy::startDragEvent( StartDragEvent* e )
214 214 p.drawText( r, Qt::AlignCenter, label );
215 215 p.end();
216 216
217   - QMimeData *mime = new QMimeData();
218   - mime->setData( "application/supercollider", QByteArray() );
  217 + QMimeData *mime = e->data;
  218 + e->data = 0; // prevent deleting the data when event destroyed;
219 219
220 220 QDrag *drag = new QDrag(w);
221 221 drag->setMimeData( mime );
@@ -370,35 +370,11 @@ bool QWidgetProxy::interpretDragEvent( QObject *o, QEvent *e, QList<QVariant> &a
370 370
371 371 QDropEvent *dnd = static_cast<QDropEvent*>(e);
372 372
373   - if( dnd->type() == QEvent::DragEnter ) {
374   - const QMimeData *data = dnd->mimeData();
375   - if( data->hasFormat( "application/supercollider" ) ) {
376   - // nothing to do; drag data is stored in QView.currentDrag
377   - return true;
378   - }
379   - else if( data->hasColor() ) {
380   - args << data->colorData();
381   - }
382   - else if( data->hasUrls() ) {
383   - QList<QUrl> urls = data->urls();
384   - if( urls.count() > 1 ) {
385   - VariantList urlArray;
386   - Q_FOREACH( QUrl url, data->urls() ) urlArray.data << url.toString();
387   - args << QVariant::fromValue<VariantList>( urlArray );
388   - }
389   - else if( urls.count() == 1 ) {
390   - args << urls[0].toString();
391   - }
392   - }
393   - else if( data->hasText() ) {
394   - args << data->text();
395   - }
396   - else {
397   - // we can't use the data, let the widget handle DnD
398   - return false;
399   - }
400   - }
401   - else {
  373 + const QMimeData *data = dnd->mimeData();
  374 + if ( !data->hasFormat( "application/supercollider" ) )
  375 + return false;
  376 +
  377 + if( dnd->type() != QEvent::DragEnter ) {
402 378 QPoint pos = dnd->pos();
403 379 args << pos.x() << pos.y();
404 380 }
7 QtCollider/QWidgetProxy.h
@@ -26,6 +26,7 @@
26 26
27 27 #include <QWidget>
28 28 #include <QAtomicInt>
  29 +#include <QMimeData>
29 30
30 31 namespace QtCollider {
31 32 struct SetFocusEvent;
@@ -123,11 +124,13 @@ struct SetAlwaysOnTopEvent : public QEvent
123 124
124 125 struct StartDragEvent : public QEvent
125 126 {
126   - StartDragEvent( const QString &string )
  127 + StartDragEvent( const QString &label_, QMimeData *data_ )
127 128 : QEvent( (QEvent::Type) QtCollider::Event_Proxy_StartDrag ),
128   - label( string )
  129 + label( label_ ), data( data_ )
129 130 {}
  131 + ~StartDragEvent() { delete data; }
130 132 QString label;
  133 + QMimeData *data;
131 134 };
132 135
133 136 }
17 QtCollider/primitives/prim_QWidget.cpp
@@ -111,11 +111,24 @@ QC_LANG_PRIMITIVE( QWidget_SetAlwaysOnTop, 1, PyrSlot *r, PyrSlot *a, VMGlobals
111 111 return errNone;
112 112 }
113 113
114   -QC_LANG_PRIMITIVE( QWidget_StartDrag, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g ) {
  114 +QC_LANG_PRIMITIVE( QWidget_StartDrag, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g ) {
115 115 QWidgetProxy *wProxy = qobject_cast<QWidgetProxy*>( Slot::toObjectProxy(r) );
116 116 if( !wProxy->compareThread() ) return QtCollider::wrongThreadError();
117 117
118   - QApplication::postEvent( wProxy, new StartDragEvent( Slot::toString(a) ) );
  118 + PyrSlot *data = a+1;
  119 + QString str = Slot::toString(a+2);
  120 +
  121 + QMimeData *mime = new QMimeData();
  122 +
  123 + mime->setData( "application/supercollider", QByteArray() );
  124 +
  125 + if( isKindOfSlot( data, class_Color ) )
  126 + mime->setColorData( QVariant(Slot::toColor(data)) );
  127 +
  128 + if( !str.isEmpty() )
  129 + mime->setText( str );
  130 +
  131 + QApplication::postEvent( wProxy, new StartDragEvent( Slot::toString(a), mime ) );
119 132
120 133 return errNone;
121 134 }
47 SCClassLibrary/QtCollider/QView.sc
@@ -393,13 +393,12 @@ QView : QObject {
393 393
394 394 canReceiveDragHandler_ { arg handler;
395 395 canReceiveDragHandler = handler;
396   - this.setEventHandler( 60, \dragEnterEvent, true );
397   - this.setEventHandler( 61, \dragMoveEvent, true );
  396 + this.setDragEventsEnabled( true );
398 397 }
399 398
400 399 receiveDragHandler_ { arg handler;
401 400 receiveDragHandler = handler;
402   - this.setEventHandler( 63, \dropEvent, true );
  401 + this.setDragEventsEnabled( true );
403 402 }
404 403
405 404 toFrontAction_ { arg aFunction;
@@ -492,7 +491,7 @@ QView : QObject {
492 491
493 492 initQView { arg parent;
494 493
495   - var handleKeyDown, handleKeyUp, overridesMouseDown;
  494 + var handleKeyDown, handleKeyUp, overridesMouseDown, handleDrag;
496 495
497 496 if (parent.notNil) {
498 497 if( parent.decorator.notNil ) { parent.decorator.place(this) }
@@ -528,12 +527,10 @@ QView : QObject {
528 527 {this.setEventHandler( QObject.wheelEvent, \mouseWheelEvent, true )};
529 528
530 529 // DnD events
531   - if( this.respondsTo(\defaultCanReceiveDrag) ) {
532   - this.setEventHandler( 60, \dragEnterEvent, true );
533   - this.setEventHandler( 61, \dragMoveEvent, true );
534   - };
535   - if( this.respondsTo(\defaultReceiveDrag) )
536   - {this.setEventHandler( 63, \dropEvent, true )};
  530 + handleDrag = this.respondsTo(\defaultCanReceiveDrag) or: {this.respondsTo(\defaultReceiveDrag)};
  531 + this.setEventHandler( 60, \dragEnterEvent, true, enabled:handleDrag );
  532 + this.setEventHandler( 61, \dragMoveEvent, true, enabled:handleDrag );
  533 + this.setEventHandler( 63, \dropEvent, true, enabled:handleDrag );
537 534 }
538 535
539 536 onCloseEvent {
@@ -629,13 +626,14 @@ QView : QObject {
629 626 }
630 627
631 628 beginDrag { arg x, y;
632   - var obj;
  629 + var obj, str;
633 630 if( beginDragAction.notNil )
634 631 { obj = beginDragAction.value( this, x, y ) }
635 632 { obj = this.tryPerform( \defaultGetDrag, x, y ) };
636 633 if( obj.notNil ) {
637 634 QView.setCurrentDrag( obj );
638   - this.prStartDrag( dragLabel ?? {obj.asString} );
  635 + str = obj.asString;
  636 + this.prStartDrag( dragLabel ?? str, obj, str );
639 637 ^true;
640 638 };
641 639 ^false;
@@ -653,32 +651,37 @@ QView : QObject {
653 651 { this.tryPerform( \defaultReceiveDrag, x, y ) };
654 652 }
655 653
656   - prStartDrag { arg label;
  654 + prStartDrag { arg label, data, dataAsString;
657 655 _QWidget_StartDrag
658 656 ^this.primitiveFailed;
659 657 }
660 658
661   - dragEnterEvent { arg data;
662   - // if the drag was initiated within SC, 'data' arg is nil
663   - // and drag data has been already stored in QView.setCurrentDrag;
664   - // otherwise 'data' arg holds the data of the drag initiated externally
665   - if( data.notNil ) { QView.setCurrentDrag(data); }
  659 + dragEnterEvent {
666 660 // always accept the event
667 661 ^true;
668 662 }
669 663
670 664 dragMoveEvent { arg x, y;
671   - var a = nil; // prevent optimizing the method away
672   - ^this.canReceiveDrag( x, y );
  665 + // make sure the event is always consumed
  666 + ^this.canReceiveDrag( x, y ).switch (
  667 + true, true,
  668 + false, false,
  669 + false
  670 + )
673 671 }
674 672
675 673 dropEvent { arg x, y;
676 674 this.receiveDrag( x, y );
677   - // Never propagate the event.
678   - // If we got to this point it should be accepted and consumed by SC.
  675 + // always accept the event
679 676 ^true
680 677 }
681 678
  679 + setDragEventsEnabled { arg enabled;
  680 + this.setEventHandlerEnabled( 60, enabled );
  681 + this.setEventHandlerEnabled( 61, enabled );
  682 + this.setEventHandlerEnabled( 63, enabled );
  683 + }
  684 +
682 685 prMapToGlobal { arg point, retPoint;
683 686 _QWidget_MapToGlobal
684 687 ^this.primitiveFailed;

0 comments on commit 503eeb9

Please sign in to comment.
Something went wrong with that request. Please try again.