1 // SCViewHolder makes it possible to add more capabilities by holding an SCView, not subclassing it
4 classvar <>consumeKeyDowns = false;// should the view by default consume keydowns
9 // subclasses need to ALWAYS use this method to set the view
11 view.onClose = { this.viewDidClose; };
13 viewDidClose { view = nil; }
16 view.remove;// will cause view.onClose
21 action { ^view.action }
22 action_ { arg f; view.action_(f) }
23 doAction { view.doAction }
24 keyDownAction_ { arg f;
25 view.keyDownAction_(f);
27 keyDownResponder { ^nil }
28 enableKeyDowns { this.keyDownAction = this.keyDownResponder }
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}) }
48 // delegate to the view
49 doesNotUnderstand { |selector ... args|
51 view.respondsTo(selector).if({
52 result = view.performList(selector, args);
53 ^(result === view).if({ this }, { result });
55 DoesNotUnderstandError(this, selector, args).throw;
61 FlowViewLayout : FlowLayout {
66 *new { arg bounds, margin, gap;
67 ^super.new(bounds,margin,gap).prInitFlowViewLayout();
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 );
85 remove { arg view, reflow = true;
88 if ( (row.size < 1) and: (row !== rows.last) ) {
104 row = row.select(_.notClosed);
106 newRows = newRows.add(row)
111 row.do { |view| super.place(view) };
112 if (row !== rows.last) { this.nextLine };
115 wouldExceedBottom { arg aBounds;
116 ^(top + aBounds.height + margin.y) > bounds.bottom
121 prInitFlowViewLayout {
122 fOnViewClose = { |view| this.remove(view); };
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
135 var autoRemoves,prevMaxHeight,prevMaxRight;
137 *layout { arg f,bounds;
139 v = this.new(nil,bounds);
143 *viewClass { ^GUI.compositeView }
144 *new { arg parent, bounds,margin,gap,windowTitle="";
145 ^super.new.init(parent, bounds,margin,gap,windowTitle);
147 init { arg argParent, bounds,margin,gap,windowTitle="";
148 var w, parentView, iMadeParent = false;
149 parent = argParent ?? {
151 GUI.window.new(windowTitle,bounds).front
153 parentView = parent.asView;
155 bounds = bounds.asRect;
156 if(iMadeParent) { bounds = bounds.moveTo(0, 0) };
158 bounds = parentView.bounds.moveTo(0,0);
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;
171 this.decorator.startRow;
173 removeOnClose { arg updater;
174 autoRemoves.add(updater);
176 hr { arg color,height=3;
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);
183 innerBounds { ^this.decorator.innerBounds }
184 bounds_ { arg b, reflow = true;
185 if(b != view.bounds,{
187 if(this.decorator.notNil,{
188 this.decorator.bounds = b.moveTo(0, 0);
189 reflow.if({ this.reflowAll; });
193 indentedRemaining { ^this.decorator.indentedRemaining }
194 used { ^this.decorator.used }
197 this.decorator.reflow;
199 // returns the new bounds
200 resizeToFit { arg reflow = false,tryParent = false;
203 if(reflow,{ this.reflowAll; });
205 used = this.decorator.used;
206 new = view.bounds.resizeTo(used.width,used.height);
209 this.decorator.bounds = new.moveTo(0, 0);
211 if(reflow,{ this.reflowAll; });
212 // its better to call reflowDeep on the parent
214 this.parent.tryPerform(\resizeToFit,reflow,tryParent);
220 this.allChildren.reverseDo({ |view|
221 if(view.isKindOf(FlowView),{
222 view.bounds_(view.bounds.resizeTo(2000,2000),false);
223 view.reflowAll.resizeToFit;
231 wouldExceedBottom { arg aBounds; ^this.decorator.wouldExceedBottom(aBounds) }
235 ^view.children.any({ arg c;
236 r.containsRect( c.bounds ).not
240 // if what you are adding is unsure how much space it is going to take
241 // then take everything ...
243 prevMaxHeight = this.decorator.maxHeight;
244 prevMaxRight = this.decorator.maxRight;
245 ^this.decorator.indentedRemaining
247 // ... and afterwards
248 // state what you actually used.
250 didUseAllocated { arg vbounds;
251 if(prevMaxHeight.isNil,{
252 Error("didUseAllocated called without prior call to allocateRemaining").throw;
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);
263 autoRemoves.do({ |updater| updater.remove });
270 autoRemoves.do({ arg u; u.remove });
272 view.tryPerform(\viewDidClose);
275 children { ^view.children }
276 decorator { ^view.decorator }
277 decorator_ { |dec| view.decorator = dec }
283 this.decorator.clear;
289 // only SCView calls these
290 prRemoveChild { |child|
291 view.prRemoveChild(child);
293 prClose { view.prClose }