deprecate SCViewHolder-layRight
[supercollider.git] / SCClassLibrary / Common / GUI / iphone / Base / SCView.sc
blob2cacb73a71cdccc310704d1d9866387f50e97865
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;
9         var <>onClose;
11         *new { arg parent, bounds;
12                 ^super.new.init(parent, bounds);
13         }
14         *viewClass { ^this }
15         *paletteExample { arg parent, bounds;
16                 ^this.new(parent, bounds);
17         }
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
25         }
27         asView { ^this }
29         bounds {
30                 ^this.getProperty(\bounds, Rect.new)
31         }
32         bounds_ { arg rect;
33                 this.setProperty(\bounds, rect)
34         }
36         visible {
37                 ^this.getProperty(\visible)
38         }
39         visible_ { arg bool;
40                 this.setProperty(\visible, bool)
41         }
43         enabled {
44                 ^this.getProperty(\enabled)
45         }
46         enabled_ { arg bool;
47                 this.setProperty(\enabled, bool)
48         }
50         canFocus {
51                 ^this.getProperty(\canFocus)
52         }
53         canFocus_ { arg bool;
54                 this.setProperty(\canFocus, bool)
55         }
56         focus { arg flag=true;
57                 _SCView_Focus
58                 ^this.primitiveFailed
59         }
60         hasFocus{
61                 _SCView_HasFocus
62                 ^this.primitiveFailed
63         }
65         focusColor_{|color|
66                 this.setProperty(\focusColor, color);
67         }
69         focusColor{
70                 ^this.getProperty(\focusColor, Color.new);
71         }
73         id {
74                 ^this.getProperty(\id)
75         }
76         id_ { arg id;
77                 this.setProperty(\id, id)
78         }
80         dragLabel_ { arg string;
81                 this.setProperty(\dragLabel, string)
82         }
84         refresh {
85                 _SCView_Refresh
86                 ^this.primitiveFailed
87         }
88         findByID { arg id;
89                 _SCView_FindByID
90                 ^this.primitiveFailed
91         }
93         isClosed { ^dataptr.isNil }
94         notClosed { ^dataptr.notNil }
95         remove {
96                 if(dataptr.notNil,{
97                         parent.prRemoveChild(this);
98                         this.prRemove;
99                         this.prClose;
100                 },{
101                         "SCView-remove : this view already removed.".debug(this);
102                 });
103         }
104         /*
105         resize behaviour in an SCCompositeView:
106                 1  2  3
107                 4  5  6
108                 7  8  9
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
121         */
122         resize {
123                 ^this.getProperty(\resize)
124         }
125         resize_ { arg resize;
126                 this.setProperty(\resize, resize)
127         }
129         background_ { arg color;
130                 background = color;
131                 this.setProperty(\background, color)
132         }
133         addAction { arg func, selector=\action;
134                 this.perform(selector.asSetter, this.perform(selector).addFunc(func));
135         }
136         removeAction { arg func, selector=\action;
137                 this.perform(selector.asSetter, this.perform(selector).removeFunc(func));
138         }
139         mouseDown{arg x, y, modifiers, buttonNumber, clickCount;
140                 mouseDownAction.value(this, x, y, modifiers, buttonNumber, clickCount);
141         }
142         mouseUp{arg x, y, modifiers;
143                 mouseUpAction.value(this, x, y, modifiers);
144         }
145         mouseMove{arg x, y, modifiers;
146                 mouseMoveAction.value(this, x, y, modifiers);
147         }
148         mouseOver{arg x, y;
149                 mouseOverAction.value(this, x, y);
150         }
152         keyDown { arg char, modifiers, unicode,keycode;
153                 globalKeyDownAction.value(this, char, modifiers, unicode, keycode);
154                 this.handleKeyDownBubbling(this, char, modifiers, unicode, keycode);
155         }
157         keyModifiersChanged{arg modifiers;
158                 this.handleKeyModifiersChangedBubbling(this,modifiers)
159         }
161         handleKeyModifiersChangedBubbling { arg view, modifiers;
162                 var result;
163                 // nil from keyDownAction --> pass it on
164                 if (keyModifiersChangedAction.isNil) {
165 //                      this.defaultKeyDownAction(char,modifiers,unicode,keycode);
166                         result = nil;
167                 }{
168                         result = keyModifiersChangedAction.value(view, modifiers);
169                 };
170                 if(result.isNil) {
171                         // call keydown action of parent view
172                         parent.handleKeyModifiersChangedBubbling(view, modifiers);
173                 };
174         }
176         defaultKeyDownAction { ^nil }
177         handleKeyDownBubbling { arg view, char, modifiers, unicode, keycode;
178                 var result;
179                 // nil from keyDownAction --> pass it on
180                 if (keyDownAction.isNil) {
181                         result = this.defaultKeyDownAction(char,modifiers,unicode,keycode);
182                 }{
183                         result = keyDownAction.value(view, char, modifiers, unicode, keycode);
184                 };
185                 if(result.isNil) {
186                         // call keydown action of parent view
187                         parent.handleKeyDownBubbling(view, char, modifiers, unicode, keycode);
188                 };
189         }
191         // sc.solar addition
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);
197         }
198         defaultKeyUpAction { ^nil }
199         handleKeyUpBubbling { arg view, char, modifiers,unicode,keycode;
200                 var result;
201                 // nil from keyDownAction --> pass it on
202                 if (keyUpAction.isNil) {
203                         result = this.defaultKeyUpAction(char,modifiers,unicode,keycode);
204                 }{
205                         result = keyUpAction.value(view, char, modifiers, unicode, keycode);
206                 };
207                 if(result.isNil) {
208                         // call keydown action of parent view
209                         parent.handleKeyUpBubbling(view, char, modifiers, unicode, keycode);
210                 };
211         }
214         beginDrag {
215                 currentDrag = if (beginDragAction.notNil)
216                 {
217                         beginDragAction.value(this)
218                 }{
219                         this.defaultGetDrag
220                 };
221                 currentDragString = currentDrag.asCompileString;
222         }
223         defaultGetDrag { ^nil }
224         canReceiveDrag {
225                 ^if(canReceiveDragHandler.notNil,{ canReceiveDragHandler.value(this) },{ this.defaultCanReceiveDrag })
226         }
227         defaultCanReceiveDrag { ^false }
228         receiveDrag {|x,y|
229                 if(receiveDragHandler.notNil,{ receiveDragHandler.value(this, x, y) },{ this.defaultReceiveDrag(x,y) });
230                 currentDrag = currentDragString = nil;
231         }
233         // get the view parent tree up to the SCTopView
234         getParents {
235                 var parents, view;
236                 view = this;
237                 parents = List.new;
238                 while({(view = view.parent).notNil},{ parents.add(view)});
239                 ^parents
240         }
242         doAction {
243                 action.value(this);
244         }
246         properties {
247                 ^#[\bounds, \visible, \enabled, \canFocus, \resize, \background,
248                                 \minWidth,\maxWidth,\minHeight,\maxHeight,\focusColor]
249         }
250         getPropertyList {
251                 ^this.properties.collect({ arg name;
252                         [name, this.perform(name)]
253                 });
254         }
255         setPropertyList { arg list;
256                 list.do({ arg item;
257                         var name, value;
258                         #name, value = item;
259                         this.perform(name.asSetter, value);
260                 });
261         }
263         // private
264         prInit { arg argParent, argBounds,argViewClass;
265                 _SCView_New
266                 ^this.primitiveFailed
267         }
268         prClose { dataptr = nil; onClose.value(this); }
269         prRemove {
270                 _SCView_Remove
271                 ^this.primitiveFailed
272         }
273         setProperty { arg key, value;
274                 _SCView_SetProperty
275                 ^this.primitiveFailed
276         }
277         getProperty { arg key, value;
278                 _SCView_GetProperty
279                 ^this.primitiveFailed
280         }
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.
285                         this.doAction;
286                 });
287         }
289 //      *importDrag {
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;
295 //      }
297         absoluteBounds {
298                 ^this.getProperty(\absoluteBounds,Rect.new)
299         }
302 SCContainerView : SCView { // abstract class
303         var <children, <decorator, < relativeOrigin = true;
305         add { arg child;
306                 children = children.add(child);
307                 if (decorator.notNil, { decorator.place(child); });
308         }
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;
316         }
318         removeAll {
319                 children.copy.do {|child| child.remove };
320         }
322         relativeOrigin_{ |bool|
323                 relativeOrigin = bool;
324                 this.setProperty(\relativeOrigin, bool);
325         }
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 ) )};
331                 ^this.decorator;
332                 }
334         decorator_ {|decor|
335                 if(relativeOrigin, {
336                         decor.bounds = decor.bounds.moveTo(0, 0);
337                         decor.reset;
338                 });
339                 decorator = decor;
340         }
342         prRemoveChild { arg child;
343                 children.remove(child);
344                 // ... decorator should re-place all
345         }
347         //bounds_  ... replace all
349         prClose {
350                 super.prClose;
351                 children.do({ arg item; item.prClose });
352         }
355 SCCompositeView : SCContainerView {
358 SCTopView : SCCompositeView {
359         // created by SCWindow
360         handleKeyModifiersChangedBubbling { arg view, modifiers;
361                 keyModifiersChangedAction.value(view, modifiers);
362         }
363         handleKeyDownBubbling { arg view, char, modifiers, unicode, keycode;
364                 var currentAppModal, window;
365                 keyDownAction.value(view, char, modifiers, unicode, keycode);
366         }
367         handleKeyUpBubbling { arg view, char, modifiers, unicode, keycode;
368                 keyUpAction.value(view, char, modifiers, unicode, keycode);
369         }
371         //only in construction mode, handled internally
372         canReceiveDrag { ^currentDrag.isKindOf(Class)}
373 //      remove { this.removeAll }
375         findWindow{
376                 SCWindow.allWindows.do {|win|
377                         if(win.view == this){
378                                 ^win
379                         }
380                 }
381         }
383         /* construction mode */
385         constructionGrid_{ arg point;
386                 this.setProperty( \constructionGrid, point );
387         }
389         constructionGrid {
390                 ^this.getProperty( \constructionGrid, Point.new );
391         }
393         enableConstructionGrid_{arg flag;
394                 this.setProperty( \enableConstructionGrid, flag );
395         }
397         enableConstructionGrid{
398                 ^this.getProperty( \enableConstructionGrid );
399         }
401         //private called from lang
402         setConstructionMode {|flag|
403                 this.setProperty( \setConstructionMode, flag )
404         }
406         defaultReceiveDrag{|x,y|
407                 var win, view;
408                 win = this.findWindow;
409                 view = currentDrag.paletteExample(win, Rect(x,y,140,24)).enabled_(false);
410                 view.keyDownAction_({|view, char, modifiers, unicode, keycode|
411                         if(keycode == 51){
412                                 view.remove;
413                         }
414                 });
415         }
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);
425         }
427         hasHorizontalScroller_{|bool|
428                 hasHorizontalScroller = bool;
429                 this.setProperty(\setHasHorizontalScroller, bool);
430         }
432         hasVerticalScroller_{|bool|
433                 hasVerticalScroller = bool;
434                 this.setProperty(\setHasVerticalScroller, bool);
435         }
437         visibleOrigin_ { arg point;
438                 this.setProperty( \clipViewOrigin, point );
439         }
441         visibleOrigin { ^this.getProperty( \clipViewOrigin, Point.new );}
443         autoScrolls_ {|bool|
444                 autoScrolls = bool;
445                 this.setProperty(\setAutoScrolls, bool);
446         }
448         innerBounds {
449                 ^this.getProperty(\innerBounds, Rect.new)
450         }
452         bounds {
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)
457                         }, {
458                                 ^bounds
459                         });
460                 });
461                 ^bounds
462         }
464 //      handleKeyModifiersChangedBubbling { arg view, modifiers;
465 //              var result;
466 //              // nil from keyDownAction --> pass it on
467 //              if (keyModifiersChangedAction.isNil) {
468 //                      result = nil;
469 //              }{
470 //                      result = keyModifiersChangedAction.value(view, modifiers);
471 //              };
472 //              if(result.isNil) {
473 //                      // call keydown action of parent view
474 //                      parent.handleKeyModifiersChangedBubbling(view, modifiers);
475 //              };
476 //      }
478 //      handleKeyDownBubbling { arg view, char, modifiers, unicode, keycode;
479 //              var result;
480 //              // nil from keyDownAction --> pass it on
481 //              if (keyDownAction.isNil) {
482 //                      result = this.defaultKeyDownAction(char,modifiers,unicode,keycode);
483 //              }{
484 //                      result = keyDownAction.value(view, char, modifiers, unicode, keycode);
485 //              };
486 //              if(result.isNil) {
487 //                      // call keydown action of parent view
488 //                      parent.handleKeyDownBubbling(view, char, modifiers, unicode, keycode);
489 //              };
490 //      }
492 //      handleKeyUpBubbling { arg view, char, modifiers,unicode,keycode;
493 //              var result;
494 //              // nil from keyDownAction --> pass it on
495 //              if (keyUpAction.isNil) {
496 //                      result = this.defaultKeyUpAction(char,modifiers,unicode,keycode);
497 //              }{
498 //                      result = keyUpAction.value(view, char, modifiers, unicode, keycode);
499 //              };
500 //              if(result.isNil) {
501 //                      // call keydown action of parent view
502 //                      parent.handleKeyUpBubbling(view, char, modifiers, unicode, keycode);
503 //              };
504 //      }
508 SCScrollView : SCScrollTopView {
509         var <hasBorder = false;
511         hasBorder_ { arg bool = true;
512                 this.setProperty(\border, bool);
513         }
515         init { |argParent, argBounds|
516                 super.init(argParent, argBounds);
518                 relativeOrigin = false; // scroll views are never relative, although they really are ;-)
519         }
521         relativeOrigin_ {  }
523         handleKeyModifiersChangedBubbling { arg view, modifiers;
524                 var result;
525                 // nil from keyDownAction --> pass it on
526                 if (keyModifiersChangedAction.isNil) {
527                         result = nil;
528                 }{
529                         result = keyModifiersChangedAction.value(view, modifiers);
530                 };
531                 if(result.isNil) {
532                         // call keydown action of parent view
533                         parent.handleKeyModifiersChangedBubbling(view, modifiers);
534                 };
535         }
537         handleKeyDownBubbling { arg view, char, modifiers, unicode, keycode;
538                 var result;
539                 // nil from keyDownAction --> pass it on
540                 if (keyDownAction.isNil) {
541                         result = this.defaultKeyDownAction(char,modifiers,unicode,keycode);
542                 }{
543                         result = keyDownAction.value(view, char, modifiers, unicode, keycode);
544                 };
545                 if(result.isNil) {
546                         // call keydown action of parent view
547                         parent.handleKeyDownBubbling(view, char, modifiers, unicode, keycode);
548                 };
549         }
551         handleKeyUpBubbling { arg view, char, modifiers,unicode,keycode;
552                 var result;
553                 // nil from keyDownAction --> pass it on
554                 if (keyUpAction.isNil) {
555                         result = this.defaultKeyUpAction(char,modifiers,unicode,keycode);
556                 }{
557                         result = keyUpAction.value(view, char, modifiers, unicode, keycode);
558                 };
559                 if(result.isNil) {
560                         // call keydown action of parent view
561                         parent.handleKeyUpBubbling(view, char, modifiers, unicode, keycode);
562                 };
563         }
567 SCLayoutView : SCContainerView {
568         properties { ^super.properties ++ #[\spacing] }
570         spacing {
571                 ^this.getProperty(\spacing, 0)
572         }
573         spacing_ { arg distance;
574                 this.setProperty(\spacing, distance)
575         }
576         setProperty { |key, value|
577                         // layout views don't recognize relativeOrigin in the backend
578                 if(key != \relativeOrigin) {
579                         super.setProperty(key, value);
580                 };
581         }
584 SCHLayoutView : SCLayoutView {}
585 SCVLayoutView : SCLayoutView {}
588 SCControlView : SCView { // abstract class
591 SCSliderBase : SCControlView {
593         knobColor {
594                 ^this.getProperty(\knobColor, Color.new)
595         }
596         knobColor_ { arg color;
597                 this.setProperty(\knobColor, color)
598         }
600         step_ { arg stepSize;
601                 this.setPropertyWithAction(\step, stepSize);
602         }
603         step {
604                 ^this.getProperty(\step)
605         }
607         properties {
608                 ^super.properties ++ #[\knobColor, \step]
609         }
613 SCSlider : SCSliderBase
615         value {
616                 ^this.getProperty(\value)
617         }
618         value_ { arg val;
619                 this.setProperty(\value, val);
620         }
621         valueAction_ { arg val;
622                 this.setPropertyWithAction(\value, val);
623         }
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) }
628         pixelStep {
629                 var bounds = this.bounds;
630                 ^(bounds.width.max(bounds.height) - this.thumbSize).reciprocal
631         }
633         defaultKeyDownAction { arg char, modifiers, unicode,keycode;
634                 // standard keydown
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
646         }
648         defaultGetDrag {
649                 ^this.value
650         }
651         defaultCanReceiveDrag {
652                 ^currentDrag.isNumber
653         }
654         defaultReceiveDrag {
655                 this.valueAction = currentDrag;
656         }
658         thumbSize {
659                 ^this.getProperty(\thumbSize, 12);
660         }
661         thumbSize_ { arg size;
662                 this.setProperty(\thumbSize, size);
663         }
665         properties {
666                 ^super.properties ++ #[\thumbSize];
667         }
670 SCRangeSlider : SCSliderBase {
672         *paletteExample { arg parent, bounds;
673                 var v;
674                 v = this.new(parent, bounds);
675                 v.lo = 0.2;
676                 v.hi = 0.7;
677                 ^v
678         }
680         lo {
681                 ^this.getProperty(\lo)
682         }
683         lo_ { arg val;
684                 this.setProperty(\lo, val);
685         }
686         activeLo_ { arg val;
687                 this.setPropertyWithAction(\lo, val);
688         }
689         hi {
690                 ^this.getProperty(\hi)
691         }
692         hi_ { arg val;
693                 this.setProperty(\hi, val);
694         }
695         activeHi_ { arg val;
696                 this.setPropertyWithAction(\hi, val);
697         }
698         range {
699                 ^this.getProperty(\range)
700         }
701         range_ { arg val;
702                 this.setProperty(\range, val);
703         }
704         activeRange_ { arg val;
705                 this.setPropertyWithAction(\range, val);
706         }
708         setSpan { arg lo, hi;
709                 this.lo = lo;
710                 this.hi = hi;
711         }
713         setSpanActive { arg lo, hi;
714                 this.setSpan( lo, hi );
715                 this.doAction;
716         }
718         setDeviation { arg deviation, average;
719                         var lo = ( 1 - deviation ) * average;
720                         this.setSpan(lo, lo + deviation);
721         }
723         properties {
724                 ^super.properties ++ #[\lo, \hi]
725         }
727         pixelStep {
728                 var bounds = this.bounds;
729                 ^(bounds.width.max(bounds.height)).reciprocal
730         }
732         increment { |zoom=1|
733                 var inc = (max(this.step, this.pixelStep) * zoom);
734                 var newHi = (this.hi + inc);
735                 if (newHi > 1) {
736                         inc = 1 - this.hi;
737                         newHi = 1;
738                 };
739                 this.lo_(this.lo + inc).activeHi_(newHi);
740         }
742         decrement { |zoom=1|
743                 var inc = (max(this.step, this.pixelStep) * zoom);
744                 var newLo = (this.lo - inc);
745                 if (newLo < 0) {
746                         inc =  this.lo;
747                         newLo = 0;
748                 };
749                 this.lo_(newLo).activeHi_(this.hi - inc);
750         }
752         defaultKeyDownAction { arg char, modifiers, unicode;
753                 var a, b;
754                 // standard keydown
755                 if (char == $r, {
756                         a = 1.0.rand;
757                         b = 1.0.rand;
758                         this.activeLo_(min(a, b));
759                         this.activeHi_(max(a, b));
760                         ^this
761                 });
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
771         }
772         defaultGetDrag { ^Point(this.lo, this.hi) }
773         defaultCanReceiveDrag {
774                 ^currentDrag.isKindOf(Point);
775         }
776         defaultReceiveDrag {
777                 // changed to x,y instead of lo, hi
778                 this.lo = currentDrag.x;
779                 this.hi = currentDrag.y;
780                 this.doAction
781         }
784 SCButton : SCControlView {
785         var <font, <states;
787         *paletteExample { arg parent, bounds;
788                 var v;
789                 v = this.new(parent, bounds);
790                 v.states = [
791                         ["Push", Color.black, Color.red],
792                         ["Pop", Color.white, Color.blue]];
793                 ^v
794         }
796         value {
797                 ^this.getProperty(\value)
798         }
799         value_ { arg val;
800                 this.setProperty(\value, val);
801         }
802         valueAction_ { arg val;
803                 this.setPropertyWithAction(\value, val);
804         }
806         doAction { arg modifiers;
807                 action.value(this, modifiers);
808         }
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
816         }
818         font_ { arg argFont;
819                 font = argFont;
820                 this.setProperty(\font, font)
821         }
823         states_ { arg array;
824                 states = array;
825                 this.setProperty(\states, states);
826         }
828         properties {
829                 ^super.properties ++ #[\value, \font, \states]
830         }
832         defaultGetDrag {
833                 ^this.value
834         }
835         defaultCanReceiveDrag {
836                 ^currentDrag.isNumber or: { currentDrag.isKindOf(Function) };
837         }
838         defaultReceiveDrag {
839                 if (currentDrag.isNumber) {
840                         this.valueAction = currentDrag;
841                 }{
842                         this.action = currentDrag;
843                 };
844         }
847 SCStaticTextBase : SCView {
848         var <string, <font, <object, <>setBoth=true;
850         font_ { arg argFont;
851                 font = argFont;
852                 this.setProperty(\font, font)
853         }
855         string_ { arg argString;
856                 string = argString.asString;
857                 this.setProperty(\string, string)
858         }
859         align_ { arg align;
860                 this.setProperty(\align, align)
861         }
863         stringColor {
864                 ^this.getProperty(\stringColor, Color.new)
865         }
866         stringColor_ { arg color;
867                 this.setProperty(\stringColor, color)
868         }
870         object_ { arg obj;
871                 object = obj;
872                 if (setBoth) { this.string = object.asString(80); };
873         }
875         properties {
876                 ^super.properties ++ #[\string, \font, \stringColor]
877         }
880 SCStaticText : SCStaticTextBase {
881         *paletteExample { arg parent, bounds;
882                 var v;
883                 v = this.new(parent, bounds);
884                 v.string = "The lazy brown fox";
885                 ^v
886         }
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;
896                 var v;
897                 v = this.new(parent, bounds);
898                 v.value = 123.456;
899                 ^v
900         }
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
909         }
911         increment { this.valueAction = this.value + step; }
912         decrement { this.valueAction = this.value - step; }
914         defaultKeyDownAction { arg char, modifiers, unicode;
915                 // standard chardown
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);
923                         });
924                         ^this
925                 });
926                 if (char == 127.asAscii, { // delete key
927                         keyString = nil;
928                         this.string = object.asString;
929                         this.stringColor = normalColor;
930                         ^this
931                 });
932                 if (char.isDecDigit || "+-.eE".includes(char), {
933                         if (keyString.isNil, {
934                                 keyString = String.new;
935                                 this.stringColor = typingColor;
936                         });
937                         keyString = keyString.add(char);
938                         this.string = keyString;
939                         ^this
940                 });
941                 ^nil            // bubble if it's an invalid key
942         }
944         value { ^object }
945         value_ { arg val;
946                 keyString = nil;
947                 this.stringColor = normalColor;
948                 object = val !? { val.clip(clipLo, clipHi) };
949                 this.string = object.asString;
950         }
951         valueAction_ { arg val;
952                 var prev;
953                 prev = object;
954                 this.value = val !? { val.clip(clipLo, clipHi) };
955                 if (object != prev, { this.doAction });
956         }
958         boxColor {
959                 this.deprecated(thisMethod, SCView.findMethod(\background));
960                 ^this.background;
961         }
962         boxColor_ { arg color;
963                 this.deprecated(thisMethod, SCView.findMethod(\background_));
964                 this.background_(color)
965         }
967         properties {
968                 ^super.properties ++ #[\boxColor]
969         }
970         defaultGetDrag {
971                 ^object.asFloat
972         }
973         defaultCanReceiveDrag {
974                 ^currentDrag.isNumber;
975         }
976         defaultReceiveDrag {
977                 this.valueAction = currentDrag;
978         }
980         mouseDown { arg x, y, modifiers, buttonNumber, clickCount;
981                 hit = Point(x,y);
982                 if (scroll == true, {
983                         inc = 1.0;
984                         case
985                                 { modifiers & 131072 == 131072 } // shift defaults to step x 0.1
986                                         { inc = shift_step }
987                                 { modifiers & 262144 == 262144 } // control defaults to step x 10
988                                         { inc = ctrl_step };
989                 });
990                 mouseDownAction.value(this, x, y, modifiers, buttonNumber, clickCount)
991         }
993         mouseMove { arg x, y, modifiers;
994                 var direction;
995                 if (scroll == true, {
996                         direction = 1.0;
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));
1001                         hit = Point(x, y);
1002                 });
1003                 mouseMoveAction.value(this, x, y, modifiers);
1004         }