scide: LookupDialog - redo lookup on classes after partial lookup
[supercollider.git] / SCClassLibrary / Common / GUI / SCViewHolder.sc
blob4342fe7fa648ed045f98fcd33cf6fb3eff9bcad4
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                 var newRows;
101                 this.reset;
102                 newRows = [];
103                 rows.do { |row,i|
104                         row = row.select(_.notClosed);
105                         if(row.isEmpty.not,{
106                                 newRows = newRows.add(row)
107                         });
108                 };
109                 rows = newRows;
110                 rows.do { |row|
111                         row.do { |view| super.place(view) };
112                         if (row !== rows.last) { this.nextLine };
113                 };
114         }
115         wouldExceedBottom { arg aBounds;
116                 ^(top + aBounds.height + margin.y) > bounds.bottom
117         }
119         rows { ^rows.copy }
121         prInitFlowViewLayout {
122                 fOnViewClose = { |view| this.remove(view); };
123         }
125         prAddRow { rows = rows.add(List.new); }
129 FlowView : SCViewHolder {
131         // a CompositeView with a FlowLayout as its decorator
132         // has the advantage that it preserves startRow when the view is resized
134         var     <parent;
135         var     autoRemoves,prevMaxHeight,prevMaxRight;
137         *layout { arg f,bounds;
138                 var v;
139                 v = this.new(nil,bounds);
140                 f.value(v);
141                 ^v
142         }
143         *viewClass { ^GUI.compositeView }
144         *new { arg parent, bounds,margin,gap,windowTitle="";
145                 ^super.new.init(parent, bounds,margin,gap,windowTitle);
146         }
147         init { arg argParent, bounds,margin,gap,windowTitle="";
148                 var w, parentView, iMadeParent = false;
149                 parent = argParent ?? {
150                         iMadeParent = true;
151                         GUI.window.new(windowTitle,bounds).front
152                 };
153                 parentView = parent.asView;
154                 if(bounds.notNil,{
155                         bounds = bounds.asRect;
156                         if(iMadeParent) { bounds = bounds.moveTo(0, 0) };
157                 },{
158                         bounds = parentView.bounds.moveTo(0,0);
159                 });
160                 this.view = this.class.viewClass.new(parentView, bounds);
162                 // parent has placed me, now get my bounds
163                 bounds = view.bounds.moveTo(0, 0);
165                 // note: FlowLayout default is 4@4 4@4
166                 view.decorator = FlowViewLayout(bounds, margin ?? {2@0}, gap ?? {4@4}, false);
167                 view.decorator.owner = this;
168                 autoRemoves = IdentitySet.new;
169         }
170         startRow {
171                 this.decorator.startRow;
172         }
173         removeOnClose { arg updater;
174                 autoRemoves.add(updater);
175         }
176         hr { arg color,height=3;
177                 this.startRow;
178                 StaticText(this,Rect(0,0,this.decorator.innerBounds.width - (2 * this.decorator.gap.x), height,0))
179                                 .string_("").background_(color ?? {Color(1,1,1,0.3)} ).resize_(2);
180                 this.startRow;
181         }
183         innerBounds { ^this.decorator.innerBounds }
184         bounds_ { arg b, reflow = true;
185                 if(b != view.bounds,{
186                         view.bounds = b;
187                         if(this.decorator.notNil,{
188                                 this.decorator.bounds = b.moveTo(0, 0);
189                                 reflow.if({ this.reflowAll; });
190                         })
191                 });
192         }
193         indentedRemaining { ^this.decorator.indentedRemaining }
194         used { ^this.decorator.used }
196         reflowAll {
197                 this.decorator.reflow;
198         }
199         // returns the new bounds
200         resizeToFit { arg reflow = false,tryParent = false;
201                 var used,new;
203                 if(reflow,{ this.reflowAll; });
205                 used = this.decorator.used;
206                 new = view.bounds.resizeTo(used.width,used.height);
207                 view.bounds = new;
209                 this.decorator.bounds = new.moveTo(0, 0);
211                 if(reflow,{ this.reflowAll; });
212                 // its better to call reflowDeep on the parent
213                 if(tryParent,{
214                         this.parent.tryPerform(\resizeToFit,reflow,tryParent);
215                 });
216                 ^new
217         }
219         reflowDeep {
220                 this.allChildren.reverseDo({ |view|
221                         if(view.isKindOf(FlowView),{
222                                 view.bounds_(view.bounds.resizeTo(2000,2000),false);
223                                 view.reflowAll.resizeToFit;
224                         });
225                 });
226         }
227         front {
228                 // window.front
229         }
231         wouldExceedBottom { arg aBounds; ^this.decorator.wouldExceedBottom(aBounds) }
232         anyChildExceeds {
233                 var r;
234                 r = view.bounds;
235                 ^view.children.any({ arg c;
236                         r.containsRect( c.bounds ).not
237                 });
238         }
240         // if what you are adding is unsure how much space it is going to take
241         // then take everything ...
242         allocateRemaining {
243                 prevMaxHeight = this.decorator.maxHeight;
244                 prevMaxRight = this.decorator.maxRight;
245                 ^this.decorator.indentedRemaining
246         }
247         // ... and afterwards
248         // state what you actually used.
249         // see FlowView-flow
250         didUseAllocated { arg vbounds;
251                 if(prevMaxHeight.isNil,{
252                         Error("didUseAllocated called without prior call to allocateRemaining").throw;
253                 });
254                 this.decorator.left = vbounds.right + this.decorator.margin.x;
255                 // maxRight is max right of all rows ever laid
256                 // but maxHeight is the max of this row only
257                 // but they both have to be rescinded
258                 this.decorator.maxRight = max( prevMaxRight, vbounds.right );
259                 this.decorator.maxHeight = max( prevMaxHeight,vbounds.height);
260         }
262         remove {
263                 autoRemoves.do({ |updater| updater.remove });
264                 autoRemoves = nil;
265                 view.notClosed.if({
266                         view.remove;
267                 });
268         }
269         viewDidClose {
270                 autoRemoves.do({ arg u; u.remove });
271                 autoRemoves = nil;
272                 view.tryPerform(\viewDidClose);
273         }
275         children { ^view.children }
276         decorator { ^view.decorator }
277         decorator_ { |dec| view.decorator = dec }
278         add { |child|
279                 view.add(child);
280         }
281         removeAll {
282                 view.removeAll;
283                 this.decorator.clear;
284         }
286         asFlowView {}
287         asPageLayout {}
289         // only SCView calls these
290         prRemoveChild { |child|
291                 view.prRemoveChild(child);
292         }
293         prClose { view.prClose }