1 // ==========================================================================
  2 // Project:   SproutCore - JavaScript Application Framework
  3 // Copyright: ©2006-2010 Sprout Systems, Inc. and contributors.
  4 //            Portions ©2008-2010 Apple Inc. All rights reserved.
  5 // License:   Licensed under MIT license (see license.js)
  6 // ==========================================================================
  7 
  8 sc_require('ext/menu_item');
  9 
 10 /**
 11   @class
 12   Extends SC.MenuPane to add support for automatic resizing.
 13 */
 14 
 15 SC.AutoResizingMenuPane = SC.MenuPane.extend(
 16 /** @scope SC.AutoResizingMenuPane.prototype */ {
 17 
 18   /**
 19     If YES, the menu should automatically resize its width to fit its items.
 20 
 21     This will swap out the default SC.MenuItemView. If you are using a custom
 22     exampleView, you will need to mix SC.AutoResize into your exampleView
 23     and set shouldAutoResize to NO (the actual resizing will be handled
 24     by SC.MenuPane).
 25 
 26     This property must be set before instantiation; any changes after instantiation
 27     will not function properly.
 28 
 29     @property
 30     @type {Boolean}
 31     @default YES
 32   */
 33   shouldAutoResize: YES,
 34 
 35   /**
 36     The minimum width for this menu if it is to be automatically resized.
 37 
 38     If no value is specified, it will be determined from the controlSize.
 39 
 40     @type Number
 41     @default minimumMenuWidth from render delegate, or 0.
 42   */
 43   minimumMenuWidth: SC.propertyFromRenderDelegate('minimumMenuWidth', 0),
 44 
 45   /**
 46     The amount to add to any calculated width.
 47 
 48     If no value is specified, it will be determined from the controlSize.
 49 
 50     @type Number
 51     @default menuWidthPadding from render delegate, or 0
 52   */
 53   menuWidthPadding: SC.propertyFromRenderDelegate('menuWidthPadding', 0),
 54 
 55   /**
 56     The view class to use when creating new menu item views.
 57 
 58     The menu pane will automatically create an instance of the view class you
 59     set here for each item in the `items` array. You may provide your own
 60     subclass for this property to display the customized content.
 61 
 62     @type SC.View
 63     @default SC.AutoResizingMenuItemView
 64   */
 65   exampleView: SC.AutoResizingMenuItemView,
 66 
 67   /**
 68     @private
 69     In addition to the normal init, we need to schedule an automatic resize.
 70   */
 71   init: function() {
 72     sc_super();
 73 
 74     if (this.get('shouldAutoResize')) {
 75       this.invokeOnce('_updateMenuWidth');
 76     }
 77   },
 78 
 79   /**
 80     The array of child menu item views that compose the menu.
 81 
 82     This computed property parses @displayItems@ and constructs an SC.MenuItemView (or whatever class you have set as the @exampleView@) for every item.
 83 
 84     @property
 85     @type Array
 86     @readOnly
 87     @private
 88   */
 89   createMenuItemViews: function() {
 90     // EXTENDED to set shouldMeasureSize to its initial value and to
 91     // observe the measured size.
 92     var views = sc_super();
 93 
 94     var idx, len = views.length, view;
 95     if (this.get('shouldAutoResize')) {
 96       for (idx = 0; idx < len; idx++) {
 97         view = views[idx];
 98 
 99         // set up resizing if we want
100         view.set('shouldMeasureSize', YES);
101         view.addObserver('measuredSize', this, this._menuItemMeasuredSizeDidChange);
102       }
103     }
104 
105     return views;
106   },
107 
108   _menuItemViewsDidChange: function() {
109     if (this.get('shouldAutoResize')) this.invokeOnce('_updateMenuWidth');
110   }.observes('menuItemViews'),
111 
112   _menuItemMeasuredSizeDidChange: function(menuItem) {
113     this.invokeOnce('_updateMenuWidth');
114   },
115 
116   _menuMinimumMenuWidthDidChange: function() {
117     this.invokeOnce('_updateMenuWidth');
118   }.observes('minimumMenuWidth'),
119 
120   _updateMenuWidth: function() {
121     var menuItemViews = this.get('menuItemViews');
122     if (!menuItemViews) return;
123 
124     var len = menuItemViews.length, idx, view,
125         width = this.get('minimumMenuWidth');
126 
127     for (idx = 0; idx < len; idx++) {
128       view = menuItemViews[idx];
129       width = Math.max(width, view.get('measuredSize').width + this.get('menuWidthPadding'));
130     }
131 
132 
133     this.adjust({ 'width': width, height: this.get('menuHeight') });
134     this.positionPane();
135   }
136 });
137