1 // ========================================================================== 2 // Project: SproutCore 3 // Copyright: @2012 7x7 Software, Inc. 4 // License: Licensed under MIT license (see license.js) 5 // ========================================================================== 6 sc_require("views/view"); 7 8 // When in debug mode, developers can log the design mode. 9 //@if (debug) 10 SC.LOG_DESIGN_MODE = false; 11 //@endif 12 13 // The class names assigned to view elements depending on the current design 14 // mode. 15 SC.DESIGN_MODE_CLASS_NAMES = { 16 s: 'sc-small', 17 m: 'sc-medium', 18 l: 'sc-large', 19 xl: 'sc-xlarge' 20 }; 21 22 /** @private This adds design modes support to SC.View. */ 23 SC.View.reopen( 24 /** @scope SC.View.prototype */ { 25 26 // ------------------------------------------------------------------------ 27 // Properties 28 // 29 30 /** 31 The current design mode of the application and this view. 32 33 If the application has designModes specified, this property will be set 34 automatically when the view is created and as the window size changes 35 across the design mode boundaries. 36 37 @property {String} 38 @default null 39 */ 40 designMode: null, 41 42 /** 43 The dynamic adjustments to apply to this view depending on the current 44 design mode. 45 46 If you specify designModes on the application, this hash will be checked 47 for a matching adjustment to apply for the current design mode. 48 49 @property {Object} 50 @default null 51 */ 52 modeAdjust: null, 53 54 // ------------------------------------------------------------------------ 55 // Methods 56 // 57 58 /** @private Recursively set the designMode on each child view. */ 59 adjustChildDesignModes: function (lastDesignMode, designMode) { 60 var childViews = this.get('childViews'); 61 62 var i, len = childViews.get('length'); 63 for (i = 0; i < len; i++) { 64 var childView = childViews.objectAt(i); 65 66 childView.updateDesignMode(lastDesignMode, designMode); 67 } 68 }, 69 70 _sc_assignProperty: function (key, value) { 71 if (key === 'layout') { 72 var newExplicitLayout = this._sc_computeExplicitLayout(value), // Convert the layout to an explicit layout. 73 layoutDiff = {}, 74 explicitLayout = this.get('explicitLayout'); 75 for (var layoutKey in newExplicitLayout) { 76 var currentValue = explicitLayout[layoutKey]; 77 78 layoutDiff[layoutKey] = currentValue === undefined ? null : currentValue; 79 80 if (layoutKey === 'centerX') { 81 layoutDiff.left = explicitLayout.left; 82 layoutDiff.right = explicitLayout.right; 83 } 84 85 if (layoutKey === 'centerY') { 86 layoutDiff.top = explicitLayout.top; 87 layoutDiff.bottom = explicitLayout.bottom; 88 } 89 } 90 91 this._originalProperties.layout = layoutDiff; 92 } else { 93 // Get the original value of the property for reset. 94 this._originalProperties[key] = this.get(key); 95 } 96 97 // Apply the override. 98 if (key === 'layout') { 99 //@if(debug) 100 if (SC.LOG_DESIGN_MODE || this.SC_LOG_DESIGN_MODE) { 101 SC.Logger.log(' - Adjusting %@: %@ (cached as %@)'.fmt(key, SC.inspect(value), SC.inspect(this._originalProperties[key]))); 102 } 103 //@endif 104 this.adjust(value); 105 } else { 106 //@if(debug) 107 if (SC.LOG_DESIGN_MODE || this.SC_LOG_DESIGN_MODE) { 108 SC.Logger.log(' - Setting %@: %@ (cached as %@)'.fmt(key, SC.inspect(value), SC.inspect(this._originalProperties[key]))); 109 } 110 //@endif 111 this.set(key,value); 112 } 113 }, 114 115 _sc_revertProperty: function (key, oldValue) { 116 //@if(debug) 117 if (SC.LOG_DESIGN_MODE || this.SC_LOG_DESIGN_MODE) { 118 SC.Logger.log(' - Resetting %@ to %@'.fmt(key, SC.inspect(oldValue))); 119 } 120 //@endif 121 122 if (key === 'layout') { 123 this.adjust(oldValue); 124 } else { 125 this.set(key, oldValue); 126 } 127 }, 128 129 /** 130 Updates the design mode for this view. 131 132 This method is called automatically by the view's pane whenever the pane 133 determines that the design mode, as specified in the pane's designModes 134 property, has changed. You should likely never need to call it manually. 135 136 This method updates the designMode property of the view, adjusts 137 the layout if a matching design adjustment in the view's designAdjustments 138 property is found and adds a class name to the view for the current 139 design mode. 140 141 Note that updating the design mode also updates all child views of this 142 view. 143 144 @param {String} lastDesignMode the previously applied design mode 145 @param {String} [designMode] the name of the design mode 146 */ 147 updateDesignMode: function (lastDesignMode, designMode) { 148 // Fast path. 149 if (lastDesignMode === designMode) { return; } 150 151 var classNames = this.get('classNames'), 152 modeAdjust, 153 elem, 154 key, 155 layer, 156 newProperties, 157 prevProperties, 158 size; 159 160 this.set('designMode', designMode); 161 162 // Get the size name portion of the mode. 163 if (designMode) { 164 size = designMode.split('_')[0]; 165 } 166 167 modeAdjust = this.get('modeAdjust'); 168 if (modeAdjust) { 169 // Stop observing changes for a moment. 170 this.beginPropertyChanges(); 171 172 // Unset any previous properties. 173 prevProperties = this._originalProperties; 174 if (prevProperties) { 175 //@if(debug) 176 if (SC.LOG_DESIGN_MODE || this.SC_LOG_DESIGN_MODE) { 177 SC.Logger.log('%@ — Removing previous design property overrides set by "%@":'.fmt(this, lastDesignMode)); 178 } 179 //@endif 180 181 for (key in prevProperties) { 182 this._sc_revertProperty(key, prevProperties[key]); 183 } 184 185 // Remove the cache. 186 this._originalProperties = null; 187 } 188 189 if (designMode) { 190 // Apply new properties. The orientation specific properties override the size properties. 191 if (modeAdjust[size] || modeAdjust[designMode]) { 192 newProperties = SC.merge(modeAdjust[size], modeAdjust[designMode]); 193 194 //@if(debug) 195 if (SC.LOG_DESIGN_MODE || this.SC_LOG_DESIGN_MODE) { 196 SC.Logger.log('%@ — Applying design properties for "%@":'.fmt(this, designMode)); 197 } 198 //@endif 199 200 // Cache the original properties for reset. 201 this._originalProperties = {}; 202 for (key in newProperties) { 203 this._sc_assignProperty(key, newProperties[key]); 204 } 205 } 206 } 207 208 // Resume observing. 209 this.endPropertyChanges(); 210 } 211 212 // Apply the design mode as a class name. 213 // This is done here rather than through classNameBindings, because we can 214 // do it here without needing to setup a designMode observer for each view. 215 var designClass; 216 layer = this.get('layer'); 217 if (layer) { 218 elem = this.$(); 219 220 // If we had previously added a class to the element, remove it. 221 if (lastDesignMode) { 222 designClass = SC.DESIGN_MODE_CLASS_NAMES[lastDesignMode.split('_')[0]]; 223 elem.removeClass(designClass); 224 classNames.removeObject(designClass); 225 } 226 227 // If necessary, add a new class. 228 if (designMode) { 229 designClass = SC.DESIGN_MODE_CLASS_NAMES[size]; 230 elem.addClass(designClass); 231 classNames.push(designClass); 232 } 233 } else { 234 if (designMode) { 235 designClass = SC.DESIGN_MODE_CLASS_NAMES[size]; 236 // Ensure that it gets into the classNames array 237 // so it is displayed when we render. 238 classNames.push(designClass); 239 } 240 } 241 242 // Set the designMode on each child view (may be null). 243 this.adjustChildDesignModes(lastDesignMode, designMode); 244 } 245 246 }); 247