1 SCView { // abstract class
2 classvar <>currentDrag, <>currentDragString;
3 classvar <>globalKeyDownAction, <>globalKeyUpAction;
5 var dataptr, <parent, <>action, <background;
6 var <>mouseDownAction, <>mouseUpAction, <>mouseOverAction, <>mouseMoveAction;
7 var <>keyDownAction, <>keyUpAction, <>keyTyped, <> keyModifiersChangedAction;
8 var <>beginDragAction,<>canReceiveDragHandler,<>receiveDragHandler;
11 *new { arg parent, bounds;
12 ^super.new.init(parent, bounds);
15 *paletteExample { arg parent, bounds;
16 ^this.new(parent, bounds);
19 init { arg argParent, argBounds;
20 parent = argParent.asView; // actual view
21 background = Color.clear;
22 // call asView again because parent by this point might be a FlowView
23 this.prInit(parent.asView, argBounds.asRect,this.class.viewClass);
24 argParent.add(this);//maybe window or viewadapter
30 ^this.getProperty(\bounds, Rect.new)
33 this.setProperty(\bounds, rect)
37 ^this.getProperty(\visible)
40 this.setProperty(\visible, bool)
44 ^this.getProperty(\enabled)
47 this.setProperty(\enabled, bool)
51 ^this.getProperty(\canFocus)
54 this.setProperty(\canFocus, bool)
56 focus { arg flag=true;
66 this.setProperty(\focusColor, color);
70 ^this.getProperty(\focusColor, Color.new);
74 ^this.getProperty(\id)
77 this.setProperty(\id, id)
80 dragLabel_ { arg string;
81 this.setProperty(\dragLabel, string)
93 isClosed { ^dataptr.isNil }
94 notClosed { ^dataptr.notNil }
97 parent.prRemoveChild(this);
101 "SCView-remove : this view already removed.".debug(this);
105 resize behaviour in an SCCompositeView:
110 1 - fixed to left, fixed to top
111 2 - horizontally elastic, fixed to top
112 3 - fixed to right, fixed to top
114 4 - fixed to left, vertically elastic
115 5 - horizontally elastic, vertically elastic
116 6 - fixed to right, vertically elastic
118 7 - fixed to left, fixed to bottom
119 8 - horizontally elastic, fixed to bottom
120 9 - fixed to right, fixed to bottom
123 ^this.getProperty(\resize)
125 resize_ { arg resize;
126 this.setProperty(\resize, resize)
129 background_ { arg color;
131 this.setProperty(\background, color)
133 addAction { arg func, selector=\action;
134 this.perform(selector.asSetter, this.perform(selector).addFunc(func));
136 removeAction { arg func, selector=\action;
137 this.perform(selector.asSetter, this.perform(selector).removeFunc(func));
139 mouseDown{arg x, y, modifiers, buttonNumber, clickCount;
140 mouseDownAction.value(this, x, y, modifiers, buttonNumber, clickCount);
142 mouseUp{arg x, y, modifiers;
143 mouseUpAction.value(this, x, y, modifiers);
145 mouseMove{arg x, y, modifiers;
146 mouseMoveAction.value(this, x, y, modifiers);
149 mouseOverAction.value(this, x, y);
152 keyDown { arg char, modifiers, unicode,keycode;
153 globalKeyDownAction.value(this, char, modifiers, unicode, keycode);
154 this.handleKeyDownBubbling(this, char, modifiers, unicode, keycode);
157 keyModifiersChanged{arg modifiers;
158 this.handleKeyModifiersChangedBubbling(this,modifiers)
161 handleKeyModifiersChangedBubbling { arg view, modifiers;
163 // nil from keyDownAction --> pass it on
164 if (keyModifiersChangedAction.isNil) {
165 // this.defaultKeyDownAction(char,modifiers,unicode,keycode);
168 result = keyModifiersChangedAction.value(view, modifiers);
171 // call keydown action of parent view
172 parent.handleKeyModifiersChangedBubbling(view, modifiers);
176 defaultKeyDownAction { ^nil }
177 handleKeyDownBubbling { arg view, char, modifiers, unicode, keycode;
179 // nil from keyDownAction --> pass it on
180 if (keyDownAction.isNil) {
181 result = this.defaultKeyDownAction(char,modifiers,unicode,keycode);
183 result = keyDownAction.value(view, char, modifiers, unicode, keycode);
186 // call keydown action of parent view
187 parent.handleKeyDownBubbling(view, char, modifiers, unicode, keycode);
192 keyUp { arg char, modifiers, unicode,keycode;
193 this.keyTyped = char;
194 // always call global keydown action first
195 globalKeyUpAction.value(this, char, modifiers, unicode, keycode);
196 this.handleKeyUpBubbling(this, char, modifiers, unicode, keycode);
198 defaultKeyUpAction { ^nil }
199 handleKeyUpBubbling { arg view, char, modifiers,unicode,keycode;
201 // nil from keyDownAction --> pass it on
202 if (keyUpAction.isNil) {
203 result = this.defaultKeyUpAction(char,modifiers,unicode,keycode);
205 result = keyUpAction.value(view, char, modifiers, unicode, keycode);
208 // call keydown action of parent view
209 parent.handleKeyUpBubbling(view, char, modifiers, unicode, keycode);
215 currentDrag = if (beginDragAction.notNil)
217 beginDragAction.value(this)
221 currentDragString = currentDrag.asCompileString;
223 defaultGetDrag { ^nil }
225 ^if(canReceiveDragHandler.notNil,{ canReceiveDragHandler.value(this) },{ this.defaultCanReceiveDrag })
227 defaultCanReceiveDrag { ^false }
229 if(receiveDragHandler.notNil,{ receiveDragHandler.value(this, x, y) },{ this.defaultReceiveDrag(x,y) });
230 currentDrag = currentDragString = nil;
233 // get the view parent tree up to the SCTopView
238 while({(view = view.parent).notNil},{ parents.add(view)});
247 ^#[\bounds, \visible, \enabled, \canFocus, \resize, \background,
248 \minWidth,\maxWidth,\minHeight,\maxHeight,\focusColor]
251 ^this.properties.collect({ arg name;
252 [name, this.perform(name)]
255 setPropertyList { arg list;
259 this.perform(name.asSetter, value);
264 prInit { arg argParent, argBounds,argViewClass;
266 ^this.primitiveFailed
268 prClose { dataptr = nil; onClose.value(this); }
271 ^this.primitiveFailed
273 setProperty { arg key, value;
275 ^this.primitiveFailed
277 getProperty { arg key, value;
279 ^this.primitiveFailed
281 setPropertyWithAction { arg symbol, obj;
282 // setting some properties may need to have the action called.
283 if (this.setProperty(symbol, obj), {
284 // setProperty returns true if action needs to be called.
290 // // this is called when an NSString is the drag object
291 // // from outside of the SC app
292 // // we compile it to an SCObject.
293 // currentDragString = currentDrag;
294 // currentDrag = currentDrag.interpret;
298 ^this.getProperty(\absoluteBounds,Rect.new)
302 SCContainerView : SCView { // abstract class
303 var <children, <decorator, < relativeOrigin = true;
306 children = children.add(child);
307 if (decorator.notNil, { decorator.place(child); });
310 init { |argParent, argBounds|
311 super.init(argParent, argBounds);
312 // if user changed default relativeOrigin to true,
313 // the client would be out of sync with the cocoa widget
314 // without resetting the view property
315 this.relativeOrigin = relativeOrigin;
319 children.copy.do {|child| child.remove };
322 relativeOrigin_{ |bool|
323 relativeOrigin = bool;
324 this.setProperty(\relativeOrigin, bool);
327 addFlowLayout { |margin, gap|
328 this.relativeOrigin.if
329 {this.decorator_( FlowLayout( this.bounds.moveTo(0,0), margin, gap ) )}
330 {this.decorator_( FlowLayout( this.bounds, margin, gap ) )};
336 decor.bounds = decor.bounds.moveTo(0, 0);
342 prRemoveChild { arg child;
343 children.remove(child);
344 // ... decorator should re-place all
347 //bounds_ ... replace all
351 children.do({ arg item; item.prClose });
355 SCCompositeView : SCContainerView {
358 SCTopView : SCCompositeView {
359 // created by SCWindow
360 handleKeyModifiersChangedBubbling { arg view, modifiers;
361 keyModifiersChangedAction.value(view, modifiers);
363 handleKeyDownBubbling { arg view, char, modifiers, unicode, keycode;
364 var currentAppModal, window;
365 keyDownAction.value(view, char, modifiers, unicode, keycode);
367 handleKeyUpBubbling { arg view, char, modifiers, unicode, keycode;
368 keyUpAction.value(view, char, modifiers, unicode, keycode);
371 //only in construction mode, handled internally
372 canReceiveDrag { ^currentDrag.isKindOf(Class)}
373 // remove { this.removeAll }
376 SCWindow.allWindows.do {|win|
377 if(win.view == this){
383 /* construction mode */
385 constructionGrid_{ arg point;
386 this.setProperty( \constructionGrid, point );
390 ^this.getProperty( \constructionGrid, Point.new );
393 enableConstructionGrid_{arg flag;
394 this.setProperty( \enableConstructionGrid, flag );
397 enableConstructionGrid{
398 ^this.getProperty( \enableConstructionGrid );
401 //private called from lang
402 setConstructionMode {|flag|
403 this.setProperty( \setConstructionMode, flag )
406 defaultReceiveDrag{|x,y|
408 win = this.findWindow;
409 view = currentDrag.paletteExample(win, Rect(x,y,140,24)).enabled_(false);
410 view.keyDownAction_({|view, char, modifiers, unicode, keycode|
418 SCScrollTopView : SCTopView {
419 var <autohidesScrollers = true, <hasHorizontalScroller = true, <hasVerticalScroller = true;
420 var <autoScrolls = true;
422 autohidesScrollers_{|bool|
423 autohidesScrollers = bool;
424 this.setProperty(\setAutohidesScrollers, bool);
427 hasHorizontalScroller_{|bool|
428 hasHorizontalScroller = bool;
429 this.setProperty(\setHasHorizontalScroller, bool);
432 hasVerticalScroller_{|bool|
433 hasVerticalScroller = bool;
434 this.setProperty(\setHasVerticalScroller, bool);
437 visibleOrigin_ { arg point;
438 this.setProperty( \clipViewOrigin, point );
441 visibleOrigin { ^this.getProperty( \clipViewOrigin, Point.new );}
445 this.setProperty(\setAutoScrolls, bool);
449 ^this.getProperty(\innerBounds, Rect.new)
453 var bounds = this.absoluteBounds;
454 this.getParents.do({ |parent|
455 (parent.tryPerform(\relativeOrigin) == true).if({
456 bounds = bounds.moveBy(parent.bounds.left.neg, parent.bounds.top.neg)
464 // handleKeyModifiersChangedBubbling { arg view, modifiers;
466 // // nil from keyDownAction --> pass it on
467 // if (keyModifiersChangedAction.isNil) {
470 // result = keyModifiersChangedAction.value(view, modifiers);
472 // if(result.isNil) {
473 // // call keydown action of parent view
474 // parent.handleKeyModifiersChangedBubbling(view, modifiers);
478 // handleKeyDownBubbling { arg view, char, modifiers, unicode, keycode;
480 // // nil from keyDownAction --> pass it on
481 // if (keyDownAction.isNil) {
482 // result = this.defaultKeyDownAction(char,modifiers,unicode,keycode);
484 // result = keyDownAction.value(view, char, modifiers, unicode, keycode);
486 // if(result.isNil) {
487 // // call keydown action of parent view
488 // parent.handleKeyDownBubbling(view, char, modifiers, unicode, keycode);
492 // handleKeyUpBubbling { arg view, char, modifiers,unicode,keycode;
494 // // nil from keyDownAction --> pass it on
495 // if (keyUpAction.isNil) {
496 // result = this.defaultKeyUpAction(char,modifiers,unicode,keycode);
498 // result = keyUpAction.value(view, char, modifiers, unicode, keycode);
500 // if(result.isNil) {
501 // // call keydown action of parent view
502 // parent.handleKeyUpBubbling(view, char, modifiers, unicode, keycode);
508 SCScrollView : SCScrollTopView {
509 var <hasBorder = false;
511 hasBorder_ { arg bool = true;
512 this.setProperty(\border, bool);
515 init { |argParent, argBounds|
516 super.init(argParent, argBounds);
518 relativeOrigin = false; // scroll views are never relative, although they really are ;-)
523 handleKeyModifiersChangedBubbling { arg view, modifiers;
525 // nil from keyDownAction --> pass it on
526 if (keyModifiersChangedAction.isNil) {
529 result = keyModifiersChangedAction.value(view, modifiers);
532 // call keydown action of parent view
533 parent.handleKeyModifiersChangedBubbling(view, modifiers);
537 handleKeyDownBubbling { arg view, char, modifiers, unicode, keycode;
539 // nil from keyDownAction --> pass it on
540 if (keyDownAction.isNil) {
541 result = this.defaultKeyDownAction(char,modifiers,unicode,keycode);
543 result = keyDownAction.value(view, char, modifiers, unicode, keycode);
546 // call keydown action of parent view
547 parent.handleKeyDownBubbling(view, char, modifiers, unicode, keycode);
551 handleKeyUpBubbling { arg view, char, modifiers,unicode,keycode;
553 // nil from keyDownAction --> pass it on
554 if (keyUpAction.isNil) {
555 result = this.defaultKeyUpAction(char,modifiers,unicode,keycode);
557 result = keyUpAction.value(view, char, modifiers, unicode, keycode);
560 // call keydown action of parent view
561 parent.handleKeyUpBubbling(view, char, modifiers, unicode, keycode);
567 SCLayoutView : SCContainerView {
568 properties { ^super.properties ++ #[\spacing] }
571 ^this.getProperty(\spacing, 0)
573 spacing_ { arg distance;
574 this.setProperty(\spacing, distance)
576 setProperty { |key, value|
577 // layout views don't recognize relativeOrigin in the backend
578 if(key != \relativeOrigin) {
579 super.setProperty(key, value);
584 SCHLayoutView : SCLayoutView {}
585 SCVLayoutView : SCLayoutView {}
588 SCControlView : SCView { // abstract class
591 SCSliderBase : SCControlView {
594 ^this.getProperty(\knobColor, Color.new)
596 knobColor_ { arg color;
597 this.setProperty(\knobColor, color)
600 step_ { arg stepSize;
601 this.setPropertyWithAction(\step, stepSize);
604 ^this.getProperty(\step)
608 ^super.properties ++ #[\knobColor, \step]
613 SCSlider : SCSliderBase
616 ^this.getProperty(\value)
619 this.setProperty(\value, val);
621 valueAction_ { arg val;
622 this.setPropertyWithAction(\value, val);
625 increment { |zoom=1| ^this.valueAction = this.value + (max(this.step, this.pixelStep) * zoom) }
626 decrement { |zoom=1| ^this.valueAction = this.value - (max(this.step, this.pixelStep) * zoom) }
629 var bounds = this.bounds;
630 ^(bounds.width.max(bounds.height) - this.thumbSize).reciprocal
633 defaultKeyDownAction { arg char, modifiers, unicode,keycode;
635 if (char == $r, { this.valueAction = 1.0.rand; ^this });
636 if (char == $n, { this.valueAction = 0.0; ^this });
637 if (char == $x, { this.valueAction = 1.0; ^this });
638 if (char == $c, { this.valueAction = 0.5; ^this });
639 if (char == $], { this.increment; ^this });
640 if (char == $[, { this.decrement; ^this });
641 if (unicode == 16rF700, { this.increment; ^this });
642 if (unicode == 16rF703, { this.increment; ^this });
643 if (unicode == 16rF701, { this.decrement; ^this });
644 if (unicode == 16rF702, { this.decrement; ^this });
645 ^nil // bubble if it's an invalid key
651 defaultCanReceiveDrag {
652 ^currentDrag.isNumber
655 this.valueAction = currentDrag;
659 ^this.getProperty(\thumbSize, 12);
661 thumbSize_ { arg size;
662 this.setProperty(\thumbSize, size);
666 ^super.properties ++ #[\thumbSize];
670 SCRangeSlider : SCSliderBase {
672 *paletteExample { arg parent, bounds;
674 v = this.new(parent, bounds);
681 ^this.getProperty(\lo)
684 this.setProperty(\lo, val);
687 this.setPropertyWithAction(\lo, val);
690 ^this.getProperty(\hi)
693 this.setProperty(\hi, val);
696 this.setPropertyWithAction(\hi, val);
699 ^this.getProperty(\range)
702 this.setProperty(\range, val);
704 activeRange_ { arg val;
705 this.setPropertyWithAction(\range, val);
708 setSpan { arg lo, hi;
713 setSpanActive { arg lo, hi;
714 this.setSpan( lo, hi );
718 setDeviation { arg deviation, average;
719 var lo = ( 1 - deviation ) * average;
720 this.setSpan(lo, lo + deviation);
724 ^super.properties ++ #[\lo, \hi]
728 var bounds = this.bounds;
729 ^(bounds.width.max(bounds.height)).reciprocal
733 var inc = (max(this.step, this.pixelStep) * zoom);
734 var newHi = (this.hi + inc);
739 this.lo_(this.lo + inc).activeHi_(newHi);
743 var inc = (max(this.step, this.pixelStep) * zoom);
744 var newLo = (this.lo - inc);
749 this.lo_(newLo).activeHi_(this.hi - inc);
752 defaultKeyDownAction { arg char, modifiers, unicode;
758 this.activeLo_(min(a, b));
759 this.activeHi_(max(a, b));
762 if (char == $n, { this.activeLo_(0.0); this.activeHi_(0.0); ^this });
763 if (char == $x, { this.activeLo_(1.0); this.activeHi_(1.0); ^this });
764 if (char == $c, { this.activeLo_(0.5); this.activeHi_(0.5); ^this });
765 if (char == $a, { this.activeLo_(0.0); this.activeHi_(1.0); ^this });
766 if (unicode == 16rF700, { this.increment; ^this });
767 if (unicode == 16rF703, { this.increment; ^this });
768 if (unicode == 16rF701, { this.decrement; ^this });
769 if (unicode == 16rF702, { this.decrement; ^this });
770 ^nil // bubble if it's an invalid key
772 defaultGetDrag { ^Point(this.lo, this.hi) }
773 defaultCanReceiveDrag {
774 ^currentDrag.isKindOf(Point);
777 // changed to x,y instead of lo, hi
778 this.lo = currentDrag.x;
779 this.hi = currentDrag.y;
784 SCButton : SCControlView {
787 *paletteExample { arg parent, bounds;
789 v = this.new(parent, bounds);
791 ["Push", Color.black, Color.red],
792 ["Pop", Color.white, Color.blue]];
797 ^this.getProperty(\value)
800 this.setProperty(\value, val);
802 valueAction_ { arg val;
803 this.setPropertyWithAction(\value, val);
806 doAction { arg modifiers;
807 action.value(this, modifiers);
810 defaultKeyDownAction { arg char, modifiers, unicode;
811 if (char == $ , { this.valueAction = this.value + 1; ^this });
812 if (char == $\r, { this.valueAction = this.value + 1; ^this });
813 if (char == $\n, { this.valueAction = this.value + 1; ^this });
814 if (char == 3.asAscii, { this.valueAction = this.value + 1; ^this });
815 ^nil // bubble if it's an invalid key
820 this.setProperty(\font, font)
825 this.setProperty(\states, states);
829 ^super.properties ++ #[\value, \font, \states]
835 defaultCanReceiveDrag {
836 ^currentDrag.isNumber or: { currentDrag.isKindOf(Function) };
839 if (currentDrag.isNumber) {
840 this.valueAction = currentDrag;
842 this.action = currentDrag;
847 SCStaticTextBase : SCView {
848 var <string, <font, <object, <>setBoth=true;
852 this.setProperty(\font, font)
855 string_ { arg argString;
856 string = argString.asString;
857 this.setProperty(\string, string)
860 this.setProperty(\align, align)
864 ^this.getProperty(\stringColor, Color.new)
866 stringColor_ { arg color;
867 this.setProperty(\stringColor, color)
872 if (setBoth) { this.string = object.asString(80); };
876 ^super.properties ++ #[\string, \font, \stringColor]
880 SCStaticText : SCStaticTextBase {
881 *paletteExample { arg parent, bounds;
883 v = this.new(parent, bounds);
884 v.string = "The lazy brown fox";
890 SCNumberBox : SCStaticTextBase {
891 var <> keyString, <>step=1;
892 var <>typingColor, <>normalColor;
893 var <>clipLo = -inf, <>clipHi = inf, hit, inc=1.0, <>scroll=true, <>shift_step=0.1, <>ctrl_step=10;
895 *paletteExample { arg parent, bounds;
897 v = this.new(parent, bounds);
902 init { arg argParent, argBounds;
903 typingColor = Color.red;
904 normalColor = Color.black;
905 background = Color.white;
906 parent = argParent.asView; // actual view
907 this.prInit(parent.asView, argBounds.asRect,this.class.viewClass);
908 argParent.add(this);//maybe window or viewadapter
911 increment { this.valueAction = this.value + step; }
912 decrement { this.valueAction = this.value - step; }
914 defaultKeyDownAction { arg char, modifiers, unicode;
916 if (unicode == 16rF700, { this.increment; ^this });
917 if (unicode == 16rF703, { this.increment; ^this });
918 if (unicode == 16rF701, { this.decrement; ^this });
919 if (unicode == 16rF702, { this.decrement; ^this });
920 if ((char == 3.asAscii) || (char == $\r) || (char == $\n), { // enter key
921 if (keyString.notNil,{ // no error on repeated enter
922 this.valueAction_(keyString.asFloat);
926 if (char == 127.asAscii, { // delete key
928 this.string = object.asString;
929 this.stringColor = normalColor;
932 if (char.isDecDigit || "+-.eE".includes(char), {
933 if (keyString.isNil, {
934 keyString = String.new;
935 this.stringColor = typingColor;
937 keyString = keyString.add(char);
938 this.string = keyString;
941 ^nil // bubble if it's an invalid key
947 this.stringColor = normalColor;
948 object = val !? { val.clip(clipLo, clipHi) };
949 this.string = object.asString;
951 valueAction_ { arg val;
954 this.value = val !? { val.clip(clipLo, clipHi) };
955 if (object != prev, { this.doAction });
959 this.deprecated(thisMethod, SCView.findMethod(\background));
962 boxColor_ { arg color;
963 this.deprecated(thisMethod, SCView.findMethod(\background_));
964 this.background_(color)
968 ^super.properties ++ #[\boxColor]
973 defaultCanReceiveDrag {
974 ^currentDrag.isNumber;
977 this.valueAction = currentDrag;
980 mouseDown { arg x, y, modifiers, buttonNumber, clickCount;
982 if (scroll == true, {
985 { modifiers & 131072 == 131072 } // shift defaults to step x 0.1
987 { modifiers & 262144 == 262144 } // control defaults to step x 10
990 mouseDownAction.value(this, x, y, modifiers, buttonNumber, clickCount)
993 mouseMove { arg x, y, modifiers;
995 if (scroll == true, {
997 // horizontal or vertical scrolling:
998 if ( (x - hit.x) < 0 or: { (y - hit.y) > 0 }) { direction = -1.0; };
1000 this.valueAction = (this.value + (inc * this.step * direction));
1003 mouseMoveAction.value(this, x, y, modifiers);