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) ) {
102 row.do { |view| super.place(view) };
103 if (row !== rows.last) { this.nextLine };
106 wouldExceedBottom { arg aBounds;
107 ^(top + aBounds.height + margin.y) > bounds.bottom
112 prInitFlowViewLayout {
113 fOnViewClose = { |view| this.remove(view); };
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
126 var autoRemoves,prevMaxHeight,prevMaxRight;
128 *layout { arg f,bounds;
130 v = this.new(nil,bounds);
134 *viewClass { ^GUI.compositeView }
135 *new { arg parent, bounds,margin,gap,windowTitle="";
136 ^super.new.init(parent, bounds,margin,gap,windowTitle);
138 init { arg argParent, bounds,margin,gap,windowTitle="";
139 var w, parentView, iMadeParent = false;
140 parent = argParent ?? {
142 GUI.window.new(windowTitle,bounds).front
144 parentView = parent.asView;
146 bounds = bounds.asRect;
147 if(iMadeParent) { bounds = bounds.moveTo(0, 0) };
149 bounds = parentView.bounds;
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;
162 this.decorator.startRow;
164 removeOnClose { arg updater;
165 autoRemoves.add(updater);
167 hr { arg color,height=3;
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);
174 innerBounds { ^this.decorator.innerBounds }
175 bounds_ { arg b, reflow = true;
176 if(b != view.bounds,{
178 if(this.decorator.notNil,{
179 this.decorator.bounds = b.moveTo(0, 0);
180 reflow.if({ this.reflowAll; });
184 indentedRemaining { ^this.decorator.indentedRemaining }
185 used { ^this.decorator.used }
188 this.decorator.reflow;
190 // returns the new bounds
191 resizeToFit { arg reflow = false,tryParent = false;
194 if(reflow,{ this.reflowAll; });
196 used = this.decorator.used;
197 new = view.bounds.resizeTo(used.width,used.height);
200 this.decorator.bounds = new.moveTo(0, 0);
202 if(reflow,{ this.reflowAll; });
203 // its better to call reflowDeep on the parent
205 this.parent.tryPerform(\resizeToFit,reflow,tryParent);
211 this.allChildren.reverseDo({ |view|
212 if(view.isKindOf(FlowView),{
213 view.bounds_(view.bounds.resizeTo(2000,2000),false);
214 view.reflowAll.resizeToFit;
222 wouldExceedBottom { arg aBounds; ^this.decorator.wouldExceedBottom(aBounds) }
226 ^view.children.any({ arg c;
227 r.containsRect( c.bounds ).not
231 // if what you are adding is unsure how much space it is going to take
232 // then take everything ...
234 prevMaxHeight = this.decorator.maxHeight;
235 prevMaxRight = this.decorator.maxRight;
236 ^this.decorator.indentedRemaining
238 // ... and afterwards
239 // state what you actually used.
241 didUseAllocated { arg vbounds;
242 if(prevMaxHeight.isNil,{
243 Error("didUseAllocated called without prior call to allocateRemaining").throw;
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);
254 autoRemoves.do({ |updater| updater.remove });
261 autoRemoves.do({ arg u; u.remove });
263 view.tryPerform(\viewDidClose);
266 children { ^view.children }
267 decorator { ^view.decorator }
268 decorator_ { |dec| view.decorator = dec }
274 this.decorator.clear;
280 prRemoveChild { |child|
281 view.prRemoveChild(child);
283 prClose { view.prClose }