1 // ========================================================================== 2 // Project: SproutCore - JavaScript Application Framework 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 sc_require("views/toolbar"); 9 10 /** @class 11 WorkspaceView manages a content view and two optional toolbars (top and bottom). 12 You want to use WorkspaceView in one of two situations: iPhone apps where the toolbars 13 need to change size automatically based on orientation (this does that, isn't that 14 handy!) and iPad apps where you would like the masterIsHidden property to pass through. 15 16 @since SproutCore 1.2 17 @extends SC.View 18 @author Alex Iskander 19 */ 20 SC.WorkspaceView = SC.View.extend( 21 /** @scope SC.WorkspaceView.prototype */ { 22 23 /** 24 @type Array 25 @default ['sc-workspace-view'] 26 @see SC.View#classNames 27 */ 28 classNames: ["sc-workspace-view"], 29 30 /** 31 @type Array 32 @default "hasTopToolbar hasBottomToolbar".w() 33 @see SC.View#displayProperties 34 */ 35 displayProperties: ["hasTopToolbar", "hasBottomToolbar"], 36 37 /** 38 @type String 39 @default 'workspaceRenderDelegate' 40 */ 41 renderDelegateName: 'workspaceRenderDelegate', 42 43 /** 44 @type SC.View 45 @default SC.ToolbarView 46 */ 47 topToolbar: SC.ToolbarView.extend(), 48 49 /** 50 @type SC.View 51 @default null 52 */ 53 bottomToolbar: null, 54 55 /** 56 The content. Must NOT be null. 57 58 @type SC.View 59 @default SC.View 60 */ 61 contentView: SC.View.extend(), 62 63 /** 64 If you want to automatically resize the toolbars like iPhone 65 apps should, set to YES. 66 67 @type Boolean 68 @default NO 69 */ 70 autoResizeToolbars: NO, 71 72 /** 73 @type Number 74 @default 44 75 */ 76 defaultToolbarSize: 44, 77 78 /** 79 @type Number 80 @default 44 81 */ 82 largeToolbarSize: 44, 83 84 /** 85 @type Number 86 @default 30 87 */ 88 smallToolbarSize: 30, 89 90 /** 91 @field 92 @type Number 93 */ 94 toolbarSize: function() { 95 if (!this.get("autoResizeToolbars")) return this.get("defaultToolbarSize"); 96 if (this.get("orientation") === SC.HORIZONTAL_ORIENTATION) return this.get("smallToolbarSize"); 97 return this.get("largeToolbarSize"); 98 }.property("autoHideMaster", "orientation"), 99 100 /** 101 Tracks the orientation of the view. Possible values: 102 103 - SC.HORIZONTAL_ORIENTATION 104 - SC.PORTRAIT_ORIENTATION 105 106 @field 107 @type String 108 @default SC.HORIZONTAL_ORIENTATION 109 */ 110 orientation: function() { 111 var f = this.get("frame"); 112 if (f.width > f.height) return SC.HORIZONTAL_ORIENTATION; 113 else return SC.VERTICAL_ORIENTATION; 114 }.property("frame").cacheable(), 115 116 /** 117 @type Boolean 118 @default NO 119 */ 120 masterIsHidden: NO, 121 122 /** @private */ 123 masterIsHiddenDidChange: function() { 124 var t, mih = this.get("masterIsHidden"); 125 if (t = this.get("topToolbar")) t.set("masterIsHidden", mih); 126 if (t = this.get("bottomToolbar")) t.set("masterIsHidden", mih); 127 }.observes("masterIsHidden"), 128 129 /// INTERNAL CODE. HERE, THERE BE MONSTERS! 130 131 /** 132 @private 133 Whenever something that affects the tiling changes (for now, just toolbarSize, but if 134 we allow dynamic changing of toolbars in future, this could include toolbars themselves), 135 we need to update the tiling. 136 */ 137 _scmd_tilePropertyDidChange: function() { 138 this.invokeOnce("_scws_tile"); 139 }.observes("toolbarSize"), 140 141 /** @private 142 Creates the child views. Specifically, instantiates master and detail views. 143 */ 144 createChildViews: function() { 145 sc_super(); 146 147 var topToolbar = this.get("topToolbar"); 148 if (topToolbar) { 149 topToolbar = this.topToolbar = this.activeTopToolbar = this.createChildView(topToolbar); 150 this.appendChild(topToolbar); 151 } 152 153 var bottomToolbar = this.get("bottomToolbar"); 154 if (bottomToolbar) { 155 bottomToolbar = this.bottomToolbar = this.activeBottomToolbar = this.createChildView(bottomToolbar); 156 this.appendChild(bottomToolbar); 157 } 158 159 var content = this.get("contentView"); 160 content = this.contentView = this.activeContentView = this.createChildView(content); 161 this.appendChild(content); 162 163 this.invokeOnce("_scws_tile"); 164 }, 165 166 /** 167 @private 168 Tiles the views as necessary. 169 */ 170 _scws_tile: function() { 171 var contentTop = 0, contentBottom = 0, 172 topToolbar = this.get("topToolbar"), 173 bottomToolbar = this.get("bottomToolbar"), 174 content = this.get("contentView"), 175 toolbarSize = this.get("toolbarSize"); 176 177 // basically, if there is a top toolbar, we position it and change contentTop. 178 if (topToolbar) { 179 topToolbar.set("layout", { 180 left: 0, right: 0, top: 0, height: toolbarSize 181 }); 182 contentTop += toolbarSize; 183 } 184 185 // same for bottom 186 if (bottomToolbar) { 187 bottomToolbar.set("layout", { 188 left: 0, right: 0, bottom: 0, height: toolbarSize 189 }); 190 contentBottom += toolbarSize; 191 } 192 193 // finally, position content 194 this.contentView.set("layout", { 195 left: 0, right: 0, top: contentTop, bottom: contentBottom 196 }); 197 }, 198 199 /** @private 200 Returns YES if a top toolbar is present. 201 */ 202 hasTopToolbar: function() { 203 if (this.get("topToolbar")) return YES; 204 return NO; 205 }.property("topToolbar").cacheable(), 206 207 /** @private 208 Returns YES if a bottom toolbar is present. 209 */ 210 hasBottomToolbar: function() { 211 if (this.get("bottomToolbar")) return YES; 212 return NO; 213 }.property("bottomToolbar").cacheable(), 214 215 /** @private 216 Called by the individual toolbar/contentView observers at runloop end when the toolbars change. 217 */ 218 childDidChange: function() { 219 this._scws_tile(); 220 }, 221 222 /** @private 223 For subclassing, this is the currently displaying top toolbar. 224 */ 225 activeTopToolbar: null, 226 227 /** @private 228 For subclassing, this is the currently displaying bottom toolbar. 229 */ 230 activeBottomToolbar: null, 231 232 /** @private 233 For subclassing, this is the currently displaying content view. 234 */ 235 activeContentView: null, 236 237 /** @private 238 Called when the top toolbar changes. It appends it, removes any old ones, and calls toolbarsDidChange. 239 240 You may want to override this if, for instance, you want to add transitions of some sort (should be trivial). 241 */ 242 topToolbarDidChange: function() { 243 var active = this.activeTopToolbar, replacement = this.get("topToolbar"); 244 if (active) { 245 if (active.createdByParent) { 246 container.removeChildAndDestroy(active); 247 } else { 248 container.removeChild(active); 249 } 250 } 251 if (replacement) { 252 this.appendChild(replacement); 253 } 254 255 this.activeTopToolbar = replacement; 256 this.invokeLast("childDidChange"); 257 }.observes("topToolbar"), 258 259 /** 260 @private 261 */ 262 bottomToolbarDidChange: function() { 263 var active = this.activeBottomToolbar, replacement = this.get("bottomToolbar"); 264 if (active) { 265 if (active.createdByParent) { 266 container.removeChildAndDestroy(active); 267 } else { 268 container.removeChild(active); 269 } 270 } 271 if (replacement) { 272 this.appendChild(replacement); 273 } 274 275 this.activeBottomToolbar = replacement; 276 this.invokeLast("childDidChange"); 277 }.observes("bottomToolbar"), 278 279 /** @private */ 280 contentViewDidChange: function() { 281 var active = this.activeContentView, replacement = this.get("contentView"); 282 if (active) { 283 if (active.createdByParent) { 284 container.removeChildAndDestroy(active); 285 } else { 286 container.removeChild(active); 287 } 288 } 289 if (replacement) { 290 this.appendChild(replacement); 291 } 292 293 this.activeContentView = replacement; 294 this.invokeLast("childDidChange"); 295 }.observes("contentView") 296 297 }); 298