clean up indentation and spacing
[supercollider.git] / SCClassLibrary / Common / GUI / SCViewHolder.sc
blob9cc8ceb6cf251489e9cae999953f8595354c4c23
1 // SCViewHolder makes it possible to add more capabilities by holding an SCView, not subclassing it
2 SCViewHolder {
4         classvar <>consumeKeyDowns = false;// should the view by default consume keydowns
6         var <view;
8         view_ { arg v;
9                 // subclasses need to ALWAYS use this method to set the view
10                 view = v;
11                 view.onClose = { this.viewDidClose; };
12         }
13         viewDidClose { view = nil; }
14         remove {
15                 if(view.notNil,{
16                         view.remove;// will cause view.onClose
17                         //view = nil;
18                 });
19         }
21         action { ^view.action }
22         action_ { arg f; view.action_(f) }
23         doAction { view.doAction }
24         keyDownAction_ { arg f;
25                 view.keyDownAction_(f);
26         }
27         keyDownResponder { ^nil }
28         enableKeyDowns { this.keyDownAction = this.keyDownResponder }
30         asView { ^view }
31         bounds { ^view.bounds }
32         bounds_ { arg b; view.bounds_(b) }
33         resize_ { arg r; view.resize_(r) }
34         enabled { ^view.enabled }
35         enabled_ { |b| view.enabled_(b) }
36         refresh { view.refresh }
37         background { ^view.background }
38         background_ { arg b; view.background_(b) }
39         focus { arg flag=true; view.focus(flag) }
40         visible_ { arg boo; view.visible = boo }
42         isClosed { ^(view.isNil or: {view.isClosed}) }
43         // should move lower
44         font_ { arg f;
45                 view.font = f;
46         }
48         // delegate to the view
49         doesNotUnderstand { |selector ... args|
50                 var     result;
51                 view.respondsTo(selector).if({
52                         result = view.performList(selector, args);
53                         ^(result === view).if({ this }, { result });
54                 }, {
55                         DoesNotUnderstandError(this, selector, args).throw;
56                 });
57         }
61 FlowViewLayout : FlowLayout {
62         
63         var rows;
64         var fOnViewClose;
66         *new { arg bounds, margin, gap;
67                 ^super.new(bounds,margin,gap).prInitFlowViewLayout();
68         }
70         clear {
71                 rows = [];
72                 this.reset;
73         }
75         place { arg view;
76                 var row;
77                 if (rows.size < 1) {this.prAddRow};
78                 rows.last.add( view );
79                 // Ensure the action is only added once: it will be removed only if already added.
80                 view.removeAction( fOnViewClose, \onClose );
81                 view.addAction( fOnViewClose, \onClose );
82                 super.place( view );
83         }
85         remove { arg view, reflow = true;
86                 rows.copy.do { |row|
87                         row.remove(view);
88                         if ( (row.size < 1) and: (row !== rows.last) ) {
89                                 rows.remove(row);
90                         };
91                 };
92         }
94         startRow {
95                 this.prAddRow;
96                 this.nextLine;
97         }
99         reflow {
100                 this.reset;
101                 rows.do { |row|
102                         row.do { |view| super.place(view) };
103                         if (row !== rows.last) { this.nextLine };
104                 };
105         }
106         wouldExceedBottom { arg aBounds;
107                 ^(top + aBounds.height + margin.y) > bounds.bottom
108         }
110         rows { ^rows.copy }
112         prInitFlowViewLayout {
113                 fOnViewClose = { |view| this.remove(view); };
114         }
116         prAddRow { rows = rows.add(List.new); }
120 FlowView : SCViewHolder {
122         // a CompositeView with a FlowLayout as its decorator
123         // has the advantage that it preserves startRow when the view is resized
125         var     <parent;
126         var     autoRemoves,prevMaxHeight,prevMaxRight;
128         *layout { arg f,bounds;
129                 var v;
130                 v = this.new(nil,bounds);
131                 f.value(v);
132                 ^v
133         }
134         *viewClass { ^GUI.compositeView }
135         *new { arg parent, bounds,margin,gap,windowTitle="";
136                 ^super.new.init(parent, bounds,margin,gap,windowTitle);
137         }
138         init { arg argParent, bounds,margin,gap,windowTitle="";
139                 var w, parentView, iMadeParent = false;
140                 parent = argParent ?? {
141                         iMadeParent = true;
142                         GUI.window.new(windowTitle,bounds).front
143                 };
144                 parentView = parent.asView;
145                 if(bounds.notNil,{
146                         bounds = bounds.asRect;
147                         if(iMadeParent) { bounds = bounds.moveTo(0, 0) };
148                 },{
149                         bounds = parentView.bounds;
150                 });
151                 this.view = this.class.viewClass.new(parentView, bounds);
153                 // parent has placed me, now get my bounds
154                 bounds = view.bounds.moveTo(0, 0);
156                 // note: FlowLayout default is 4@4 4@4
157                 view.decorator = FlowViewLayout(bounds, margin ?? {2@0}, gap ?? {4@4}, false);
158                 view.decorator.owner = this;
159                 autoRemoves = IdentitySet.new;
160         }
161         startRow {
162                 this.decorator.startRow;
163         }
164         removeOnClose { arg updater;
165                 autoRemoves.add(updater);
166         }
167         hr { arg color,height=3;
168                 this.startRow;
169                 StaticText(this,Rect(0,0,this.decorator.innerBounds.width - (2 * this.decorator.gap.x), height,0))
170                                 .string_("").background_(color ?? {Color(1,1,1,0.3)} ).resize_(2);
171                 this.startRow;
172         }
174         innerBounds { ^this.decorator.innerBounds }
175         bounds_ { arg b, reflow = true;
176                 if(b != view.bounds,{
177                         view.bounds = b;
178                         if(this.decorator.notNil,{
179                                 this.decorator.bounds = b.moveTo(0, 0);
180                                 reflow.if({ this.reflowAll; });
181                         })
182                 });
183         }
184         indentedRemaining { ^this.decorator.indentedRemaining }
185         used { ^this.decorator.used }
187         reflowAll {
188                 this.decorator.reflow;
189         }
190         // returns the new bounds
191         resizeToFit { arg reflow = false,tryParent = false;
192                 var used,new;
194                 if(reflow,{ this.reflowAll; });
196                 used = this.decorator.used;
197                 new = view.bounds.resizeTo(used.width,used.height);
198                 view.bounds = new;
200                 this.decorator.bounds = new.moveTo(0, 0);
202                 if(reflow,{ this.reflowAll; });
203                 // its better to call reflowDeep on the parent
204                 if(tryParent,{
205                         this.parent.tryPerform(\resizeToFit,reflow,tryParent);
206                 });
207                 ^new
208         }
210         reflowDeep {
211                 this.allChildren.reverseDo({ |view|
212                         if(view.isKindOf(FlowView),{
213                                 view.bounds_(view.bounds.resizeTo(2000,2000),false);
214                                 view.reflowAll.resizeToFit;
215                         });
216                 });
217         }
218         front {
219                 // window.front
220         }
222         wouldExceedBottom { arg aBounds; ^this.decorator.wouldExceedBottom(aBounds) }
223         anyChildExceeds {
224                 var r;
225                 r = view.bounds;
226                 ^view.children.any({ arg c;
227                         r.containsRect( c.bounds ).not
228                 });
229         }
231         // if what you are adding is unsure how much space it is going to take
232         // then take everything ...
233         allocateRemaining {
234                 prevMaxHeight = this.decorator.maxHeight;
235                 prevMaxRight = this.decorator.maxRight;
236                 ^this.decorator.indentedRemaining
237         }
238         // ... and afterwards
239         // state what you actually used.
240         // see FlowView-flow
241         didUseAllocated { arg vbounds;
242                 if(prevMaxHeight.isNil,{
243                         Error("didUseAllocated called without prior call to allocateRemaining").throw;
244                 });
245                 this.decorator.left = vbounds.right + this.decorator.margin.x;
246                 // maxRight is max right of all rows ever laid
247                 // but maxHeight is the max of this row only
248                 // but they both have to be rescinded
249                 this.decorator.maxRight = max( prevMaxRight, vbounds.right );
250                 this.decorator.maxHeight = max( prevMaxHeight,vbounds.height);
251         }
253         remove {
254                 autoRemoves.do({ |updater| updater.remove });
255                 autoRemoves = nil;
256                 view.notClosed.if({
257                         view.remove;
258                 });
259         }
260         viewDidClose {
261                 autoRemoves.do({ arg u; u.remove });
262                 autoRemoves = nil;
263                 view.tryPerform(\viewDidClose);
264         }
266         children { ^view.children }
267         decorator { ^view.decorator }
268         decorator_ { |dec| view.decorator = dec }
269         add { |child|
270                 view.add(child);
271         }
272         removeAll {
273                 view.removeAll;
274                 this.decorator.clear;
275         }
277         asFlowView {}
278         asPageLayout {}
280         prRemoveChild { |child|
281                 view.prRemoveChild(child);
282         }
283         prClose { view.prClose }