old quark gui: openOS is not osx only
[supercollider.git] / SCClassLibrary / QtCollider / QView.sc
blob35124aa48ebad80ead47f8da0e210552da398c01
1 QView : QObject {
2   classvar <globalKeyDownAction, <globalKeyUpAction;
3   classvar <hSizePolicy;
4   classvar <vSizePolicy;
5   // drag-and-drop
6   classvar <currentDrag, <currentDragString;
8   var wasRemoved = false;
9   // general props
10   var <font, <resize = 1, <alpha = 1.0;
11   // container props
12   var <decorator, <layout;
13   // top window props
14   var <>userCanClose=true, <>deleteOnClose = true;
15   // actions
16   var <action;
17   var <mouseDownAction, <mouseUpAction, <mouseOverAction, <mouseLeaveAction;
18   var <mouseMoveAction, <mouseWheelAction;
19   var <keyDownAction, <keyUpAction, <keyModifiersChangedAction;
20   var <>keyTyped;
21   // focus
22   var <focusGainedAction, <focusLostAction;
23   // drag-and-drop
24   var <>dragLabel;
25   var <beginDragAction, <canReceiveDragHandler, <receiveDragHandler;
26   // window actions
27   var <toFrontAction, <endFrontAction;
28   // hooks
29   var <onClose;
31   *initClass {
32     hSizePolicy = [1,2,3,1,2,3,1,2,3];
33     vSizePolicy = [1,1,1,2,2,2,3,3,3];
34   }
36   *new { arg parent, bounds;
37     var p = parent.asView;
38     ^super.new( [p, bounds.asRect] ).initQView( p );
39   }
41   *newCustom { arg customArgs;
42     ^super.new( customArgs ).initQView( nil );
43   }
45   *qtClass { ^'QcDefaultWidget' }
47   asView { ^this }
49   refresh {
50     _QWidget_Refresh
51     ^this.primitiveFailed;
52   }
54   remove {
55     this.destroy;
56     wasRemoved = true;
57     this.children.do { |child| child.remove };
58   }
60   mapToGlobal { arg point;
61     _QWidget_MapToGlobal
62     ^this.primitiveFailed;
63   }
65   // ----------------- properties --------------------------
66   font_ { arg f;
67     font = f;
68     this.setProperty( \font, f );
69   }
71   toolTip { ^this.getProperty(\toolTip) }
72   toolTip_ { arg t; this.setProperty( \toolTip, t ) }
74   palette {
75     ^this.getProperty( \palette );
76   }
78   palette_ { arg p;
79     this.setProperty( \palette, p );
80   }
82   background {
83     ^this.palette.windowColor;
84   }
86   background_ { arg color;
87     this.setProperty( \palette, this.palette.windowColor_(color) );
88     this.setProperty( \autoFillBackground, true );
89   }
91   // NOTE: only for backwards compatibility
92   backColor_ { arg color;
93     this.background = color;
94   }
96   absoluteBounds {
97     ^this.bounds.moveToPoint( this.mapToGlobal( 0@0 ) );
98   }
100   bounds {
101     ^this.getProperty(\geometry)
102   }
104   bounds_ { arg rect;
105     this.setProperty(\geometry, rect.asRect )
106   }
108   sizeHint { ^this.getProperty(\sizeHint) }
110   minSizeHint { ^this.getProperty(\minimumSizeHint) }
112   // a Point can be passed instead of a Size
113   maxSize_ { arg size;
114     var max = QLimits(\maxWidgetSize);
115     size = size.asSize;
116     this.setProperty( \maximumSize, Size( min(max,size.width), min(max,size.height) ) );
117   }
119   // a Point can be passed instead of a Size
120   minSize_ { arg size; this.setProperty( \minimumSize, size.asSize ); }
122   fixedSize_ { arg size;
123     size = size.asSize;
124     this.setProperty( \minimumSize, size );
125     this.setProperty( \maximumSize, size );
126   }
128   maxWidth_ { arg width;
129     this.setProperty( \maximumWidth, min( width, QLimits(\maxWidgetSize) ) );
130   }
132   minWidth_ { arg width; this.setProperty( \minimumWidth, width ); }
134   maxHeight_ { arg height;
135     this.setProperty( \maximumHeight, min( height, QLimits(\maxWidgetSize) ) );
136   }
138   minHeight_ { arg height; this.setProperty( \minimumHeight, height ); }
140   // backwards compatibility
141   relativeOrigin { ^true }
143   moveTo { arg x, y;
144     this.bounds_( this.bounds.moveTo( x, y ) );
145   }
147   resizeTo { arg width, height;
148     this.bounds_( this.bounds.resizeTo( width, height ) );
149   }
151   visible {
152     ^this.getProperty(\visible)
153   }
154   visible_ { arg bool;
155     this.setProperty(\visible, bool, false)
156   }
158   enabled {
159     ^this.getProperty(\enabled)
160   }
161   enabled_ { arg bool;
162     this.setProperty(\enabled, bool)
163   }
165   resize_ { arg anInt;
166     this.setProperty(\_qc_hSizePolicy, hSizePolicy[anInt-1]);
167     this.setProperty(\_qc_vSizePolicy, vSizePolicy[anInt-1]);
168   }
170   canFocus {
171     var policy = this.getProperty(\focusPolicy);
172     ^( policy > 0 )
173   }
175   canFocus_ { arg bool;
176     var policy;
177     if( bool ) { policy = 16r1 | 16r2 | 16r8 } { policy = 0 };
178     this.setProperty(\focusPolicy, policy);
179   }
181   focus { arg flag=true;
182     _QWidget_SetFocus
183     ^this.primitiveFailed;
184   }
186   hasFocus {
187     ^this.getProperty( \focus );
188   }
190   focusColor_ {
191     this.nonimpl( "focusColor_" );
192   }
194   focusColor {
195     this.nonimpl( "focusColor" );
196     ^Color.new;
197   }
199   // ------------------ container stuff ----------------------------
201   children { arg class = QView;
202     var ch = super.children( class );
203     ^ch.select { |v| (v.tryPerform(\isClosed) ? false).not };
204   }
206   parent { arg class = QView;
207     if (wasRemoved) { ^nil } { ^super.parent(class) };
208   }
210   parents {
211     var allParents;
212     var p;
213     if (wasRemoved.not) {
214       p = this.parent;
215       while { p.notNil } {
216         allParents = allParents.add( p );
217         p = p.parent;
218       };
219     };
220     ^allParents;
221   }
223   getParents {
224     ^this.parents;
225   }
227   removeAll {
228     var childWidgets = this.children( QView );
229     childWidgets.do { |child| child.remove };
230   }
232   layout_ { arg newLayout;
233     if( newLayout.notNil && (newLayout != layout) ) {
234       this.prSetLayout( newLayout );
235       layout = newLayout;
236     };
237   }
239   addFlowLayout { arg margin, gap;
240     this.decorator_( FlowLayout( this.bounds.moveTo(0, 0), margin, gap ) );
241     ^this.decorator;
242   }
244   decorator_ { arg decor;
245     decor.bounds = decor.bounds.moveTo(0, 0);
246     decor.reset;
247     decorator = decor;
248   }
250   flow { arg func, bounds;
251     var f;
252     f = FlowView( this, bounds );
253     func.value( f );
254     f.resizeToFit;
255     ^f;
256   }
258   // ................. top window stuff ............................
260   name {
261     ^this.getProperty( \windowTitle );
262   }
264   name_ { arg string;
265     this.setProperty( \windowTitle, string );
266   }
268   front {
269     _QWidget_BringFront
270     ^this.primitiveFailed;
271   }
273   minimize {
274     if( this.visible ) { this.invokeMethod( \showMinimized, synchronous:false ) };
275   }
277   unminimize {
278     if( this.getProperty( \minimized ) ) { this.invokeMethod( \showNormal, synchronous:false ) };
279   }
281   fullScreen {
282     this.invokeMethod( \showFullScreen, synchronous:false );
283   }
285   endFullScreen {
286     if( this.getProperty( \fullScreen ) ) { this.invokeMethod( \showNormal, synchronous:false ) };
287   }
289   alpha_ { arg aFloat;
290     alpha = aFloat;
291     this.setProperty( \windowOpacity, aFloat );
292   }
294   alwaysOnTop {
295     _QWidget_GetAlwaysOnTop
296     ^this.primitiveFailed;
297   }
299   alwaysOnTop_ { arg boolean;
300     _QWidget_SetAlwaysOnTop
301     ^this.primitiveFailed;
302   }
304   close {
305     if( deleteOnClose )
306       { this.remove; }
307       { this.visible_( false ); }
308   }
310   isClosed {
311     if (wasRemoved) {^true} {^this.isValid.not};
312   }
314   notClosed { ^this.isClosed.not }
316   // ----------------- actions .....................................
318   action_ { arg func;
319     // NOTE: not all widgets have action() signal
320     try {
321       this.manageMethodConnection( action, func, 'action()', \doAction );
322     };
323     action = func;
324   }
326   addAction { arg func, selector=\action;
327     this.perform(selector.asSetter, this.perform(selector).addFunc(func));
328   }
330   removeAction { arg func, selector=\action;
331     this.perform(selector.asSetter, this.perform(selector).removeFunc(func));
332   }
334   *globalKeyDownAction_ { arg action;
335     globalKeyDownAction = action;
336     this.setGlobalEventEnabled( 16r01 /* KeyPress */, true );
337   }
340   *globalKeyUpAction_ { arg action;
341     globalKeyUpAction = action;
342     this.setGlobalEventEnabled( 16r02 /* KeyRelease */, true );
343   }
345   keyDownAction_ { arg aFunction;
346     keyDownAction = aFunction;
347     this.setEventHandlerEnabled( QObject.keyDownEvent, true );
348   }
350   keyUpAction_ { arg aFunction;
351     keyUpAction = aFunction;
352     this.setEventHandlerEnabled( QObject.keyUpEvent, true );
353   }
355   keyModifiersChangedAction_ { arg aFunction;
356     keyModifiersChangedAction = aFunction;
357     this.setEventHandlerEnabled( QObject.keyDownEvent, true );
358     this.setEventHandlerEnabled( QObject.keyUpEvent, true );
359   }
361   mouseDownAction_ { arg aFunction;
362     mouseDownAction = aFunction;
363     this.setEventHandler( QObject.mouseDownEvent, \mouseDownEvent, true );
364     this.setEventHandler( QObject.mouseDblClickEvent, \mouseDownEvent, true );
365   }
367   mouseUpAction_ { arg aFunction;
368     mouseUpAction = aFunction;
369     this.setEventHandler( QObject.mouseUpEvent, \mouseUpEvent, true );
370   }
372   mouseMoveAction_ { arg aFunction;
373     mouseMoveAction = aFunction;
374     this.setEventHandler( QObject.mouseMoveEvent, \mouseMoveEvent, true );
375   }
377   mouseOverAction_ { arg aFunction;
378     mouseOverAction = aFunction;
379     this.setEventHandler( QObject.mouseOverEvent, \mouseOverEvent, true );
380   }
382   mouseLeaveAction_ { arg aFunction;
383     mouseLeaveAction = aFunction;
384     this.setEventHandler( QObject.mouseLeaveEvent, \mouseLeaveEvent, true );
385   }
387   mouseWheelAction_ { arg aFunction;
388     mouseWheelAction = aFunction;
389     this.setEventHandler( QObject.mouseWheelEvent, \mouseWheelEvent, true );
390   }
392   beginDragAction_ { arg handler;
393     beginDragAction = handler;
394     this.setEventHandler( QObject.mouseDownEvent, \mouseDownEvent, true )
395   }
397   canReceiveDragHandler_ { arg handler;
398     canReceiveDragHandler = handler;
399     this.setDragEventsEnabled( true );
400   }
402   receiveDragHandler_ { arg handler;
403     receiveDragHandler = handler;
404     this.setDragEventsEnabled( true );
405   }
407   toFrontAction_ { arg aFunction;
408     toFrontAction = aFunction;
409     this.setEventHandler( QObject.windowActivateEvent,
410                                \onWindowActivateEvent );
411   }
413   endFrontAction_ { arg aFunction;
414     endFrontAction = aFunction;
415     this.setEventHandler( QObject.windowDeactivateEvent,
416                                \onWindowDeactivateEvent );
417   }
419   focusGainedAction_ { arg handler;
420     focusGainedAction = handler;
421     this.setEventHandler( 8 /* QEvent::FocusIn */, \focusInEvent );
422   }
424   focusLostAction_ { arg handler;
425     focusLostAction = handler;
426     this.setEventHandler( 9 /* QEvent::FocusOut */, \focusOutEvent );
427   }
429   onClose_ { arg func;
430     this.manageFunctionConnection( onClose, func, 'destroyed()', false );
431     onClose = func;
432   }
434   doAction {
435     action.value(this);
436   }
438   defaultKeyDownAction { arg char, modifiers, unicode, keycode; }
440   defaultKeyUpAction { arg char, modifiers, unicode, keycode; }
442   keyDown { arg char, modifiers, unicode, keycode;
443     if( keyDownAction.notNil ) {
444       ^keyDownAction.value( this, char, modifiers, unicode, keycode );
445     } {
446       ^this.defaultKeyDownAction( char, modifiers, unicode, keycode );
447     };
448   }
450   keyUp { arg char, modifiers, unicode, keycode;
451     keyTyped = char;
452     if( keyUpAction.notNil ) {
453       ^keyUpAction.value( this, char, modifiers, unicode, keycode );
454     } {
455       ^this.defaultKeyUpAction( char, modifiers, unicode, keycode );
456     };
457   }
459   keyModifiersChanged { arg modifiers;
460     keyModifiersChangedAction.value( this, modifiers);
461   }
463   mouseDown { arg x, y, modifiers, buttonNumber, clickCount;
464     ^mouseDownAction.value( this, x, y, modifiers, buttonNumber, clickCount );
465   }
467   mouseUp { arg x, y, modifiers, buttonNumber;
468     ^mouseUpAction.value( this, x, y, modifiers, buttonNumber );
469   }
471   mouseMove { arg x, y, modifiers;
472     ^mouseMoveAction.value( this, x, y, modifiers );
473   }
475   mouseOver { arg x, y;
476     ^mouseOverAction.value( this, x, y );
477   }
479   mouseLeave { arg x, y;
480     ^mouseLeaveAction.value( this, x, y );
481   }
483   mouseWheel { arg x, y, modifiers, xDelta, yDelta;
484     ^mouseWheelAction.value( this, x, y, modifiers, xDelta, yDelta );
485   }
487   /* ---------------- private ----------------------- */
489   *prSetCurrentDrag { arg obj; currentDrag = obj; currentDragString = obj.asCompileString; }
490   *prClearCurrentDrag { currentDrag = nil; currentDragString = nil; }
492   *setGlobalEventEnabled { arg event, enabled;
493     _QWidget_SetGlobalEventEnabled
494   }
496   initQView { arg parent;
498     var handleKeyDown, handleKeyUp, overridesMouseDown, handleDrag;
500     if (parent.notNil) {
501         if( parent.decorator.notNil ) { parent.decorator.place(this) }
502     };
504     this.setEventHandler( QObject.closeEvent, \onCloseEvent, true );
506     // key events
507     handleKeyDown = handleKeyUp = this.overrides( \keyModifiersChanged );
508     if( handleKeyDown.not )
509       { handleKeyDown = this.overrides( \defaultKeyDownAction ) };
510     if( handleKeyUp.not )
511       { handleKeyUp = this.overrides( \defaultKeyUpAction )};
513     this.setEventHandler( QObject.keyDownEvent, \keyDownEvent, true, enabled: handleKeyDown );
514     this.setEventHandler( QObject.keyUpEvent, \keyUpEvent, true, enabled: handleKeyUp );
516     // mouse events
517     overridesMouseDown = this.overrides( \mouseDown );
518     if( this.respondsTo(\defaultGetDrag) || overridesMouseDown )
519       {this.setEventHandler( QObject.mouseDownEvent, \mouseDownEvent, true )};
520     if( overridesMouseDown )
521       {this.setEventHandler( QObject.mouseDblClickEvent, \mouseDownEvent, true )};
522     if( this.overrides( \mouseUp ) )
523       {this.setEventHandler( QObject.mouseUpEvent, \mouseUpEvent, true )};
524     if( this.overrides( \mouseMove ) )
525       {this.setEventHandler( QObject.mouseMoveEvent, \mouseMoveEvent, true )};
526     if( this.overrides( \mouseOver ) )
527       {this.setEventHandler( QObject.mouseOverEvent, \mouseOverEvent, true )};
528     if( this.overrides( \mouseLeave ) )
529       {this.setEventHandler( QObject.mouseLeaveEvent, \mouseLeaveEvent, true )};
530     if( this.overrides( \mouseWheel ) )
531       {this.setEventHandler( QObject.wheelEvent, \mouseWheelEvent, true )};
533     // DnD events
534     handleDrag = this.respondsTo(\defaultCanReceiveDrag) or: {this.respondsTo(\defaultReceiveDrag)};
535     this.setEventHandler( 60, \dragEnterEvent, true, enabled:handleDrag );
536     this.setEventHandler( 61, \dragMoveEvent, true, enabled:handleDrag );
537     this.setEventHandler( 63, \dropEvent, true, enabled:handleDrag );
538   }
540   onCloseEvent {
541     if( userCanClose != false ) {
542         if( deleteOnClose != false ) { this.remove; ^true };
543     }{
544         ^false;
545     };
546   }
548   onWindowActivateEvent {
549     toFrontAction.value(this);
550   }
552   onWindowDeactivateEvent {
553     endFrontAction.value(this);
554   }
556   focusInEvent { focusGainedAction.value(this) }
557   focusOutEvent { focusLostAction.value(this) }
559   keyDownEvent { arg char, modifiers, unicode, keycode, spontaneous;
560     modifiers = QKeyModifiers.toCocoa(modifiers);
562     if( char.size > 0 ) {char = char[0]} {char = 0.asAscii};
564     if( spontaneous ) {
565       // this event has never been propagated to parent yet
566       QView.globalKeyDownAction.value( this, char, modifiers, unicode, keycode );
567     };
569     if( (keycode == 16r1000020) || (keycode == 16r1000021) ||
570         (keycode == 16r1000022) || (keycode == 16r1000023 ) )
571       { this.keyModifiersChanged( modifiers ) };
573     ^this.keyDown( char, modifiers, unicode, keycode );
574   }
576   keyUpEvent { arg char, modifiers, unicode, keycode, spontaneous;
577     modifiers = QKeyModifiers.toCocoa(modifiers);
579     if( char.size > 0 ) {char = char[0]} {char = 0.asAscii};
581     if( spontaneous ) {
582       // this event has never been propagated to parent yet
583       QView.globalKeyUpAction.value( this, char, modifiers, unicode, keycode );
584     };
586     if( (keycode == 16r1000020) || (keycode == 16r1000021) ||
587         (keycode == 16r1000022) || (keycode == 16r1000023 ) )
588       { this.keyModifiersChanged( modifiers ) };
590     ^this.keyUp( char, modifiers, unicode, keycode );
591   }
593   mouseDownEvent { arg x, y, modifiers, buttonNumber, clickCount;
594     // WARNING: QDragView and QListView override this method!
596     if( (modifiers & QKeyModifiers.control) > 0 ) { // if Ctrl / Cmd mod
597       // Try to get drag obj and start a drag.
598       // If successful, block further processing of this event.
599       if( this.beginDrag( x, y ) ) { ^false };
600     };
602     // else continue to handle mouse down event
603     modifiers = QKeyModifiers.toCocoa(modifiers);
604     ^this.mouseDown( x, y, modifiers, buttonNumber, clickCount );
605   }
607   mouseUpEvent { arg x, y, modifiers, buttonNumber;
608     modifiers = QKeyModifiers.toCocoa(modifiers);
609     ^this.mouseUp(  x, y, modifiers, buttonNumber );
610   }
612   mouseMoveEvent { arg x, y, modifiers;
613     modifiers = QKeyModifiers.toCocoa(modifiers);
614     ^this.mouseMove( x, y, modifiers );
615   }
617   mouseOverEvent { arg x, y;
618     var dummy = x; // prevent this method from being optimized away
619     ^this.mouseOver( x, y );
620   }
622   mouseLeaveEvent { arg x, y;
623     var dummy = x; // prevent this method from being optimized away
624     ^this.mouseLeave( x, y );
625   }
627   mouseWheelEvent { arg x, y, modifiers, xDelta, yDelta;
628     modifiers = QKeyModifiers.toCocoa(modifiers);
629     ^this.mouseWheel( x, y, modifiers, xDelta, yDelta );
630   }
632   beginDrag { arg x, y;
633     var obj, str;
634     if( beginDragAction.notNil )
635       { obj = beginDragAction.value( this, x, y ) }
636       { obj = this.tryPerform( \defaultGetDrag, x, y ) };
637     if( obj.notNil ) {
638       QView.prSetCurrentDrag( obj );
639       str = obj.asString;
640       this.prStartDrag( dragLabel ?? str, obj, str );
641       ^true;
642     };
643     ^false;
644   }
646   canReceiveDrag { arg x, y;
647     if( canReceiveDragHandler.notNil )
648       { ^this.canReceiveDragHandler.value( this, x, y ) }
649       { ^( this.tryPerform( \defaultCanReceiveDrag, x, y ) ? false ) };
650   }
652   receiveDrag { arg x, y;
653     if( receiveDragHandler.notNil )
654       { this.receiveDragHandler.value( this, x, y ) }
655       { this.tryPerform( \defaultReceiveDrag, x, y ) };
656   }
658   prStartDrag { arg label, data, dataAsString;
659     _QWidget_StartDrag
660     ^this.primitiveFailed;
661   }
663   dragEnterEvent {
664     // always accept the event
665     ^true;
666   }
668   dragMoveEvent { arg x, y;
669     // make sure the event is always consumed
670     ^this.canReceiveDrag( x, y ).switch (
671       true, true,
672       false, false,
673       false
674     )
675   }
677   dropEvent { arg x, y;
678     this.receiveDrag( x, y );
679     // always accept the event
680     ^true
681   }
683   setDragEventsEnabled { arg enabled;
684     this.setEventHandlerEnabled( 60, enabled );
685     this.setEventHandlerEnabled( 61, enabled );
686     this.setEventHandlerEnabled( 63, enabled );
687   }
689   prSetLayout { arg layout;
690     _QWidget_SetLayout
691     ^this.primitiveFailed;
692   }
694   manageMethodConnection { arg oldAction, newAction, signal, method, sync=false;
695     if( newAction !== oldAction ) {
696       case
697         { oldAction.isNil && newAction.notNil } {this.connectMethod (signal, method, sync)}
698         { oldAction.notNil && newAction.isNil } {this.disconnectMethod (signal, method)}
699       ;
700     };
701   }
703   manageFunctionConnection { arg oldAction, newAction, signal, sync=false;
704     if( newAction !== oldAction ) {
705       if( oldAction.notNil ) {this.disconnectFunction (signal, oldAction)};
706       if( newAction.notNil ) {this.connectFunction (signal, newAction, sync)};
707     };
708   }
710   overrides { arg symMethod;
711     ^ ( this.class.findRespondingMethodFor(symMethod) !=
712         QView.findRespondingMethodFor(symMethod) );
713   }
715   nonimpl { arg methodName;
716     this.class.nonimpl( methodName );
717   }
719   *nonimpl { arg methodName;
720     this.debug( msg: methodName.asString ++ " is not implemented yet" )
721   }
723   *debug { arg level = 1, msg = "";
724     if( QtGUI.debugLevel >= level ) { ("Qt: " ++ this.asString ++ ": " ++ msg).postln }
725   }