1 /**
  2  * The Render Engine
  3  * FieldGroup
  4  *
  5  * @fileoverview A group of UI controls.
  6  *
  7  * @author: Brett Fattori (brettf@renderengine.com)
  8  *
  9  * @author: $Author: bfattori $
 10  * @version: $Revision: 1555 $
 11  *
 12  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 13  *
 14  * Permission is hereby granted, free of charge, to any person obtaining a copy
 15  * of this software and associated documentation files (the "Software"), to deal
 16  * in the Software without restriction, including without limitation the rights
 17  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 18  * copies of the Software, and to permit persons to whom the Software is
 19  * furnished to do so, subject to the following conditions:
 20  *
 21  * The above copyright notice and this permission notice shall be included in
 22  * all copies or substantial portions of the Software.
 23  *
 24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 29  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 30  * THE SOFTWARE.
 31  *
 32  */
 33 
 34 // The class this file defines and its required classes
 35 R.Engine.define({
 36     "class":"R.ui.FieldGroup",
 37     "requires":[
 38         "R.ui.AbstractUIControl",
 39         "R.struct.Container"
 40     ]
 41 });
 42 
 43 /**
 44  * @class A physical grouping of UI controls.
 45  *
 46  * @constructor
 47  * @param label {String} The label for the field group.
 48  * @extends R.ui.AbstractUIControl
 49  */
 50 R.ui.FieldGroup = function () {
 51     return R.ui.AbstractUIControl.extend(/** @scope R.ui.FieldGroup.prototype */{
 52 
 53         label:null,
 54         labelPosition:0,
 55         controls:null,
 56 
 57         /** @private */
 58         constructor:function (label, textRenderer) {
 59             this.base("FieldGroup", textRenderer);
 60             this.addClass("fieldgroup");
 61             this.setLabel(label || "");
 62             this.labelPosition = R.ui.FieldGroup.LABEL_TOPLEFT;
 63             this.controls = R.struct.Container.create("UIControls");
 64         },
 65 
 66         /**
 67          * Destroy the text input control, releasing its event handlers.
 68          */
 69         destroy:function () {
 70             this.base();
 71             this.controls.cleanUp();
 72         },
 73 
 74         /**
 75          * Releases the object back into the object pool.  See {@link R.engine.PooledObject#release}
 76          * for more information.
 77          */
 78         release:function () {
 79             this.base();
 80             this.label = null;
 81             this.labelPosition = R.ui.FieldGroup.LABEL_TOPLEFT;
 82             this.controls = null;
 83         },
 84 
 85         /**
 86          * Set the label for the field group, or an empty string to show no label.
 87          * @param label {String} The label
 88          */
 89         setLabel:function (label) {
 90             this.label = label;
 91         },
 92 
 93         /**
 94          * Get the field group label.
 95          * @return {String}
 96          */
 97         getLabel:function () {
 98             return this.label;
 99         },
100 
101         /**
102          * Add a control to this field group.
103          * @param uiControl {R.ui.AbstractUIControl} The control to add
104          */
105         addControl:function (uiControl) {
106             Assert(uiControl instanceof R.ui.AbstractUIControl, "You can only add UI controls to a field group");
107             this.controls.add(uiControl);
108         },
109 
110         /**
111          * Remove a control from this field group.
112          * @param uiControl {R.ui.AbstractUIControl} The control to remove
113          * @return {R.ui.AbstractUIControl} The control removed
114          */
115         removeControl:function (uiControl) {
116             return this.controls.remove(uiControl);
117         },
118 
119         /**
120          * Get the first control, within the field group, which has the specified name,
121          * or <code>null</code> if no control with the name is in the group.
122          * @param controlName {String} The name of the control to get
123          * @return {R.ui.AbstractUIControl} The control, or <code>null</code>
124          */
125         getControlByName:function (controlName) {
126             var controls = this.controls.filter(function (c) {
127                 return (c.getControlName() === controlName);
128             });
129             if (controls.length != 0) {
130                 return controls[0];
131             } else {
132                 return null;
133             }
134         },
135 
136         /**
137          * Draw the field group within the context.
138          * @param renderContext {R.rendercontexts.RenderContext2D} The render context where the control is
139          *    drawn.
140          * @param worldTime {Number} The current world time, in milliseconds
141          * @param dt {Number} The time since the last frame was drawn by the engine, in milliseconds
142          */
143         drawControl:function (renderContext, worldTime, dt) {
144             // Draw the current input text.  The text baseline is the bottom of the font,
145             // so we need to move that down by the height of the control (with some padding to look right)
146             renderContext.pushTransform();
147 
148             if (this.label != "") {
149                 this.getTextRenderer().setText(this.label);
150 
151                 // Draw the label
152                 var labelPos = R.math.Point2D.create(0, 0), wBox = R.clone(this.getWorldBox()),
153                     textWidth = this.getTextRenderer().getBoundingBox().w,
154                     textHeight = this.getTextRenderer().getBoundingBox().h;
155                 switch (this.labelPosition) {
156                     case R.ui.FieldGroup.LABEL_TOPLEFT:
157                         labelPos.x = wBox.x + 10;
158                         labelPos.y = wBox.y + 1;
159                         break;
160                     case R.ui.FieldGroup.LABEL_TOPRIGHT:
161                         labelPos.x = (wBox.x + wBox.w) - (textWidth + 10);
162                         labelPos.y = wBox.y + 1;
163                         break;
164                     case R.ui.FieldGroup.LABEL_BOTTOMLEFT:
165                         labelPos.x = wBox.x + 10;
166                         labelPos.y = (wBox.y + wBox.h) + textHeight - 2;
167                         break;
168                     case R.ui.FieldGroup.LABEL_BOTTOMRIGHT:
169                         labelPos.x = (wBox.x + wBox.w) - (textWidth + 10);
170                         labelPos.y = (wBox.y + wBox.h) + textHeight - 2;
171                         break;
172                 }
173 
174                 renderContext.pushTransform();
175                 renderContext.setPosition(labelPos);
176                 this.getTextRenderer().update(renderContext, worldTime, dt);
177                 renderContext.popTransform();
178 
179                 labelPos.destroy();
180                 wBox.destroy();
181             }
182 
183             // Render the controls in the group
184             var itr = this.controls.iterator();
185             while (itr.hasNext()) {
186                 var control = itr.next();
187                 if (control.getRenderContext() == null) {
188                     control.setRenderContext(renderContext);
189                 }
190                 control.update(renderContext, worldTime, dt);
191             }
192             itr.destroy();
193 
194             renderContext.popTransform();
195         }
196 
197     }, /** @scope R.ui.FieldGroup.prototype */{
198 
199         LABEL_TOPLEFT:0,
200 
201         LABEL_TOPRIGHT:1,
202 
203         LABEL_BOTTOMLEFT:2,
204 
205         LABEL_BOTTOMRIGHT:3,
206 
207         /**
208          * Get the class name of this object
209          * @return {String} The string "R.ui.FieldGroup"
210          */
211         getClassName:function () {
212             return "R.ui.FieldGroup";
213         },
214 
215         /**
216          * Get a properties object with values for the given object.
217          * @param obj {R.ui.FieldGroup} The field group to query
218          * @param [defaults] {Object} Default values that don't need to be serialized unless
219          *    they are different.
220          * @return {Object}
221          */
222         serialize:function (obj, defaults) {
223             var fg = R.ui.AbstractUIControl.serialize(obj, defaults),
224                 itr = obj.controls.iterator();
225 
226             fg.CONTROLS = {};
227             while (itr.hasNext()) {
228                 var control = itr.next();
229                 fg.CONTROLS[control.getControlName()] = control.constructor.serialize(control);
230             }
231             itr.destroy();
232             return fg;
233         },
234 
235         /**
236          * Deserialize the object back into a field group.
237          * @param obj {Object} The object to deserialize
238          * @param [clazz] {Class} The object class to populate
239          * @return {R.ui.ButtonControl} The object which was deserialized
240          */
241         deserialize:function (obj, clazz) {
242             // Get the controls for the group
243             var controls;
244             if (obj.CONTROLS) {
245                 controls = obj.CONTROLS;
246                 delete obj.CONTROLS;
247             }
248 
249             // Now we can deserialize the class
250             clazz = clazz || R.ui.FieldGroup.create();
251             R.ui.AbstractUIControl.deserialize(obj, clazz);
252 
253             // Re-populate the controls into the form
254             for (var c in controls) {
255                 // Grab the classname field so we can recreate the object
256                 var control = controls[c], controlClazz = R.classForName(control.CLASSNAME),
257                     uiControl = controlClazz.deserialize(control);
258 
259                 // Add the control which was deserialized
260                 clazz.addControl(uiControl);
261             }
262 
263             return clazz;
264         }
265     });
266 
267 };