Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / resizer / SplitterTracker.js
1 /**
2  * @class Ext.resizer.SplitterTracker
3  * @extends Ext.dd.DragTracker
4  * Private utility class for Ext.Splitter.
5  * @private
6  */
7 Ext.define('Ext.resizer.SplitterTracker', {
8     extend: 'Ext.dd.DragTracker',
9     requires: ['Ext.util.Region'],
10     enabled: true,
11
12     getPrevCmp: function() {
13         var splitter = this.getSplitter();
14         return splitter.previousSibling();
15     },
16
17     getNextCmp: function() {
18         var splitter = this.getSplitter();
19         return splitter.nextSibling();
20     },
21
22     // ensure the tracker is enabled, store boxes of previous and next
23     // components and calculate the constrain region
24     onBeforeStart: function(e) {
25         var prevCmp = this.getPrevCmp(),
26             nextCmp = this.getNextCmp();
27
28         // SplitterTracker is disabled if any of its adjacents are collapsed.
29         if (nextCmp.collapsed || prevCmp.collapsed) {
30             return false;
31         }
32         // store boxes of previous and next
33         this.prevBox  = prevCmp.getEl().getBox();
34         this.nextBox  = nextCmp.getEl().getBox();
35         this.constrainTo = this.calculateConstrainRegion();
36     },
37
38     // We move the splitter el. Add the proxy class.
39     onStart: function(e) {
40         var splitter = this.getSplitter();
41         splitter.addCls(splitter.baseCls + '-active');
42     },
43
44     // calculate the constrain Region in which the splitter el may be moved.
45     calculateConstrainRegion: function() {
46         var splitter   = this.getSplitter(),
47             topPad     = 0,
48             bottomPad  = 0,
49             splitWidth = splitter.getWidth(),
50             defaultMin = splitter.defaultSplitMin,
51             orient     = splitter.orientation,
52             prevBox    = this.prevBox,
53             prevCmp    = this.getPrevCmp(),
54             nextBox    = this.nextBox,
55             nextCmp    = this.getNextCmp(),
56             // prev and nextConstrainRegions are the maximumBoxes minus the
57             // minimumBoxes. The result is always the intersection
58             // of these two boxes.
59             prevConstrainRegion, nextConstrainRegion;
60
61         // vertical splitters, so resizing left to right
62         if (orient === 'vertical') {
63
64             // Region constructor accepts (top, right, bottom, left)
65             // anchored/calculated from the left
66             prevConstrainRegion = Ext.create('Ext.util.Region',
67                 prevBox.y,
68                 // Right boundary is x + maxWidth if there IS a maxWidth.
69                 // Otherwise it is calculated based upon the minWidth of the next Component
70                 (prevCmp.maxWidth ? prevBox.x + prevCmp.maxWidth : nextBox.right - (nextCmp.minWidth || defaultMin)) + splitWidth,
71                 prevBox.bottom,
72                 prevBox.x + (prevCmp.minWidth || defaultMin)
73             );
74             // anchored/calculated from the right
75             nextConstrainRegion = Ext.create('Ext.util.Region',
76                 nextBox.y,
77                 nextBox.right - (nextCmp.minWidth || defaultMin),
78                 nextBox.bottom,
79                 // Left boundary is right - maxWidth if there IS a maxWidth.
80                 // Otherwise it is calculated based upon the minWidth of the previous Component
81                 (nextCmp.maxWidth ? nextBox.right - nextCmp.maxWidth : prevBox.x + (prevBox.minWidth || defaultMin)) - splitWidth
82             );
83         } else {
84             // anchored/calculated from the top
85             prevConstrainRegion = Ext.create('Ext.util.Region',
86                 prevBox.y + (prevCmp.minHeight || defaultMin),
87                 prevBox.right,
88                 // Bottom boundary is y + maxHeight if there IS a maxHeight.
89                 // Otherwise it is calculated based upon the minWidth of the next Component
90                 (prevCmp.maxHeight ? prevBox.y + prevCmp.maxHeight : nextBox.bottom - (nextCmp.minHeight || defaultMin)) + splitWidth,
91                 prevBox.x
92             );
93             // anchored/calculated from the bottom
94             nextConstrainRegion = Ext.create('Ext.util.Region',
95                 // Top boundary is bottom - maxHeight if there IS a maxHeight.
96                 // Otherwise it is calculated based upon the minHeight of the previous Component
97                 (nextCmp.maxHeight ? nextBox.bottom - nextCmp.maxHeight : prevBox.y + (prevCmp.minHeight || defaultMin)) - splitWidth,
98                 nextBox.right,
99                 nextBox.bottom - (nextCmp.minHeight || defaultMin),
100                 nextBox.x
101             );
102         }
103
104         // intersection of the two regions to provide region draggable
105         return  prevConstrainRegion.intersect(nextConstrainRegion);
106     },
107
108     // Performs the actual resizing of the previous and next components
109     performResize: function(e) {
110         var offset   = this.getOffset('dragTarget'),
111             splitter = this.getSplitter(),
112             orient   = splitter.orientation,
113             prevCmp  = this.getPrevCmp(),
114             nextCmp  = this.getNextCmp(),
115             owner    = splitter.ownerCt,
116             layout   = owner.getLayout();
117
118         // Inhibit automatic container layout caused by setSize calls below.
119         owner.suspendLayout = true;
120
121         if (orient === 'vertical') {
122             if (prevCmp) {
123                 if (!prevCmp.maintainFlex) {
124                     delete prevCmp.flex;
125                     prevCmp.setSize(this.prevBox.width + offset[0], prevCmp.getHeight());
126                 }
127             }
128             if (nextCmp) {
129                 if (!nextCmp.maintainFlex) {
130                     delete nextCmp.flex;
131                     nextCmp.setSize(this.nextBox.width - offset[0], nextCmp.getHeight());
132                 }
133             }
134         // verticals
135         } else {
136             if (prevCmp) {
137                 if (!prevCmp.maintainFlex) {
138                     delete prevCmp.flex;
139                     prevCmp.setSize(prevCmp.getWidth(), this.prevBox.height + offset[1]);
140                 }
141             }
142             if (nextCmp) {
143                 if (!nextCmp.maintainFlex) {
144                     delete nextCmp.flex;
145                     nextCmp.setSize(prevCmp.getWidth(), this.nextBox.height - offset[1]);
146                 }
147             }
148         }
149         delete owner.suspendLayout;
150         layout.onLayout();
151     },
152
153     // perform the resize and remove the proxy class from the splitter el
154     onEnd: function(e) {
155         var splitter = this.getSplitter();
156         splitter.removeCls(splitter.baseCls + '-active');
157         this.performResize();
158     },
159
160     // Track the proxy and set the proper XY coordinates
161     // while constraining the drag
162     onDrag: function(e) {
163         var offset    = this.getOffset('dragTarget'),
164             splitter  = this.getSplitter(),
165             splitEl   = splitter.getEl(),
166             orient    = splitter.orientation;
167
168         if (orient === "vertical") {
169             splitEl.setX(this.startRegion.left + offset[0]);
170         } else {
171             splitEl.setY(this.startRegion.top + offset[1]);
172         }
173     },
174
175     getSplitter: function() {
176         return Ext.getCmp(this.getDragCt().id);
177     }
178 });