Форк Rambox
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

293 lines
10 KiB

/**
* Private utility class for Ext.Splitter.
* @private
*/
Ext.define('Ext.resizer.SplitterTracker', {
extend: 'Ext.dd.DragTracker',
requires: ['Ext.util.Region'],
enabled: true,
overlayCls: Ext.baseCSSPrefix + 'resizable-overlay',
createDragOverlay: function () {
var overlay,
El = Ext.dom.Element;
overlay = this.overlay = Ext.getBody().createChild({
role: 'presentation',
cls: this.overlayCls,
html: ' '
});
overlay.unselectable();
overlay.setSize(El.getDocumentWidth(), El.getDocumentHeight());
overlay.show();
},
getPrevCmp: function() {
var splitter = this.getSplitter();
return splitter.previousSibling(':not([hidden])');
},
getNextCmp: function() {
var splitter = this.getSplitter();
return splitter.nextSibling(':not([hidden])');
},
// ensure the tracker is enabled, store boxes of previous and next
// components and calculate the constrain region
onBeforeStart: function(e) {
var me = this,
prevCmp = me.getPrevCmp(),
nextCmp = me.getNextCmp(),
collapseEl = me.getSplitter().collapseEl,
target = e.getTarget(),
box;
if (!prevCmp || !nextCmp) {
return false;
}
if (collapseEl && target === collapseEl.dom) {
return false;
}
// SplitterTracker is disabled if any of its adjacents are collapsed.
if (nextCmp.collapsed || prevCmp.collapsed) {
return false;
}
// store boxes of previous and next
me.prevBox = prevCmp.getEl().getBox();
me.nextBox = nextCmp.getEl().getBox();
me.constrainTo = box = me.calculateConstrainRegion();
if (!box) {
return false;
}
return box;
},
// We move the splitter el. Add the proxy class.
onStart: function(e) {
var splitter = this.getSplitter();
this.createDragOverlay();
splitter.addCls(splitter.baseCls + '-active');
},
onResizeKeyDown: function(e) {
var me = this,
splitter = me.getSplitter(),
key = e.getKey(),
incrIdx = splitter.orientation === 'vertical' ? 0 : 1,
incr = key === e.UP || key === e.LEFT ? -1 : 1,
easing;
if (!me.active && me.onBeforeStart(e)) {
Ext.fly(e.target).on('keyup', me.onResizeKeyUp, me);
me.triggerStart(e);
me.onMouseDown(e);
me.startXY = splitter.getXY();
me.lastKeyDownXY = Ext.Array.slice(me.startXY);
// Movement increment eases to 4 over two seconds.
easing = me.easing = new Ext.fx.easing.Linear();
easing.setStartTime(Ext.Date.now());
easing.setStartValue(1);
easing.setEndValue(4);
easing.setDuration(2000);
}
if (me.active) {
me.lastKeyDownXY[incrIdx] = Math.round(me.lastKeyDownXY[incrIdx] + (incr * me.easing.getValue()));
me.lastXY = me.lastKeyDownXY;
splitter.setXY(me.getXY('dragTarget'));
}
},
onResizeKeyUp: function(e) {
this.onMouseUp(e);
},
// calculate the constrain Region in which the splitter el may be moved.
calculateConstrainRegion: function() {
var me = this,
splitter = me.getSplitter(),
splitWidth = splitter.getWidth(),
defaultMin = splitter.defaultSplitMin,
orient = splitter.orientation,
prevBox = me.prevBox,
prevCmp = me.getPrevCmp(),
nextBox = me.nextBox,
nextCmp = me.getNextCmp(),
// prev and nextConstrainRegions are the maximumBoxes minus the
// minimumBoxes. The result is always the intersection
// of these two boxes.
prevConstrainRegion, nextConstrainRegion, constrainOptions;
// vertical splitters, so resizing left to right
if (orient === 'vertical') {
constrainOptions = {
prevCmp: prevCmp,
nextCmp: nextCmp,
prevBox: prevBox,
nextBox: nextBox,
defaultMin: defaultMin,
splitWidth: splitWidth
};
// Region constructor accepts (top, right, bottom, left)
// anchored/calculated from the left
prevConstrainRegion = new Ext.util.Region(
prevBox.y,
me.getVertPrevConstrainRight(constrainOptions),
prevBox.bottom,
me.getVertPrevConstrainLeft(constrainOptions)
);
// anchored/calculated from the right
nextConstrainRegion = new Ext.util.Region(
nextBox.y,
me.getVertNextConstrainRight(constrainOptions),
nextBox.bottom,
me.getVertNextConstrainLeft(constrainOptions)
);
} else {
// anchored/calculated from the top
prevConstrainRegion = new Ext.util.Region(
prevBox.y + (prevCmp.minHeight || defaultMin),
prevBox.right,
// Bottom boundary is y + maxHeight if there IS a maxHeight.
// Otherwise it is calculated based upon the minWidth of the next Component
(prevCmp.maxHeight ? prevBox.y + prevCmp.maxHeight : nextBox.bottom - (nextCmp.minHeight || defaultMin)) + splitWidth,
prevBox.x
);
// anchored/calculated from the bottom
nextConstrainRegion = new Ext.util.Region(
// Top boundary is bottom - maxHeight if there IS a maxHeight.
// Otherwise it is calculated based upon the minHeight of the previous Component
(nextCmp.maxHeight ? nextBox.bottom - nextCmp.maxHeight : prevBox.y + (prevCmp.minHeight || defaultMin)) - splitWidth,
nextBox.right,
nextBox.bottom - (nextCmp.minHeight || defaultMin),
nextBox.x
);
}
// intersection of the two regions to provide region draggable
return prevConstrainRegion.intersect(nextConstrainRegion);
},
// Performs the actual resizing of the previous and next components
performResize: function(e, offset) {
var me = this,
splitter = me.getSplitter(),
orient = splitter.orientation,
prevCmp = me.getPrevCmp(),
nextCmp = me.getNextCmp(),
owner = splitter.ownerCt,
flexedSiblings = owner.query('>[flex]'),
len = flexedSiblings.length,
vertical = orient === 'vertical',
i = 0,
dimension = vertical ? 'width' : 'height',
totalFlex = 0,
item, size;
// Convert flexes to pixel values proportional to the total pixel width of all flexes.
for (; i < len; i++) {
item = flexedSiblings[i];
size = vertical ? item.getWidth() : item.getHeight();
totalFlex += size;
item.flex = size;
}
offset = vertical ? offset[0] : offset[1];
if (prevCmp) {
size = me.prevBox[dimension] + offset;
if (prevCmp.flex) {
prevCmp.flex = size;
} else {
prevCmp[dimension] = size;
}
}
if (nextCmp) {
size = me.nextBox[dimension] - offset;
if (nextCmp.flex) {
nextCmp.flex = size;
} else {
nextCmp[dimension] = size;
}
}
owner.updateLayout();
},
// Cleans up the overlay (if we have one) and calls the base. This cannot be done in
// onEnd, because onEnd is only called if a drag is detected but the overlay is created
// regardless (by onBeforeStart).
endDrag: function () {
var me = this;
if (me.overlay) {
me.overlay.destroy();
delete me.overlay;
}
me.callParent(arguments); // this calls onEnd
},
// perform the resize and remove the proxy class from the splitter el
onEnd: function(e) {
var me = this,
splitter = me.getSplitter();
splitter.removeCls(splitter.baseCls + '-active');
me.performResize(e, me.getResizeOffset());
},
// Track the proxy and set the proper XY coordinates
// while constraining the drag
onDrag: function(e) {
var me = this,
offset = me.getOffset('dragTarget'),
splitter = me.getSplitter(),
splitEl = splitter.getEl(),
orient = splitter.orientation;
if (orient === "vertical") {
splitEl.setX(me.startRegion.left + offset[0]);
} else {
splitEl.setY(me.startRegion.top + offset[1]);
}
},
getSplitter: function() {
return this.splitter;
},
getVertPrevConstrainRight: function(o) {
// Right boundary is x + maxWidth if there IS a maxWidth.
// Otherwise it is calculated based upon the minWidth of the next Component
return (o.prevCmp.maxWidth ? o.prevBox.x + o.prevCmp.maxWidth :
o.nextBox.right - (o.nextCmp.minWidth || o.defaultMin)) + o.splitWidth;
},
getVertPrevConstrainLeft: function(o) {
return o.prevBox.x + (o.prevCmp.minWidth || o.defaultMin);
},
getVertNextConstrainRight: function(o) {
return o.nextBox.right - (o.nextCmp.minWidth || o.defaultMin);
},
getVertNextConstrainLeft: function(o) {
// Left boundary is right - maxWidth if there IS a maxWidth.
// Otherwise it is calculated based upon the minWidth of the previous Component
return (o.nextCmp.maxWidth ? o.nextBox.right - o.nextCmp.maxWidth :
o.prevBox.x + (o.prevBox.minWidth || o.defaultMin)) - o.splitWidth;
},
getResizeOffset: function() {
return this.getOffset('dragTarget');
}
});