PDA

View Full Version : Ext.ux.WindowSnap.DD - Window Edge Snapping!



xantus
14 Dec 2008, 4:56 AM
I created a window edge snapping user extension.
There are two modes, snap while dragging and snap on drop. (with animation)

It's not perfect, but I believe in "release early, release often"

Demo and Source: http://xant.us/ext-ux/examples/window-edge-snap/
License: Same as Extjs 2.0

Enjoy!

Oh, btw. Comet Desktop (http://cometdesktop.com/) is open source now. I will release the code soon, but I need time to clean it up and export it to Google code subversion. See the Comet Desktop project (http://code.google.com/p/cometdesktop/) for details, and where to sign up for updates.

mjlecomte
14 Dec 2008, 7:03 AM
Nice work. I was expecting the windows to snap to the border edge, not other windows....before I actually read your instructions. So I guess that might be the next feature request for your ux, config option to snap to browser edge.

xantus
14 Dec 2008, 10:11 AM
Nice work. I was expecting the windows to snap to the border edge, not other windows....before I actually read your instructions. So I guess that might be the next feature request for your ux, config option to snap to browser edge.

I use constrain: true if I want windows to stay within the browser's area, but that's a good point. I'll add that.

Animal
14 Dec 2008, 11:17 AM
That's nice! It's like playing with magnets.:D

mjlecomte
14 Dec 2008, 12:05 PM
I use constrain: true if I want windows to stay within the browser's area, but that's a good point. I'll add that.

I think skype does something like that. I can't say I've noticed anything else that has that behavior.

Thought occurred to me, so I'll relay it to you... You might want some kind of config for browser resize as to whether the windows remain snapped to the browser edge or if they stay where they were before browser resize.

xantus
14 Dec 2008, 1:18 PM
I think skype does something like that. I can't say I've noticed anything else that has that behavior.

Thought occurred to me, so I'll relay it to you... You might want some kind of config for browser resize as to whether the windows remain snapped to the browser edge or if they stay where they were before browser resize.

I was just thinking about that! I was thinking along the lines of pairing up edges of windows and preserving the link until it is broken. That would include window edges. What do you think?

mjlecomte
14 Dec 2008, 1:51 PM
sure. it's a little addicting playing with it and resizing the windows, etc. :)

xantus
14 Dec 2008, 2:44 PM
sure. it's a little addicting playing with it and resizing the windows, etc. :)

I added a slider for the snapRange, so you can play with the magnetism like qualities of the edges.

haha, it is addicting.

xantus
14 Dec 2008, 2:53 PM
I'm also reworking the algorithm that determines where the window edges are. It's primitive now... It's only using x and y ortho.

jay@moduscreate.com
14 Dec 2008, 5:13 PM
Great work. definitely creative!

mjlecomte
14 Dec 2008, 6:20 PM
Slider is nice to play with the effect. The recent version appears to have changed the drag behavior now, where before it was smooth drag (if the snap was on or off I think) and now it kind of moves on a grid and is a bit choppy.

Hopefully you are saving these in versions. You might want to save your versions as different demos perhaps so people can test the various implementations you have done.

You're still messing around I imagine...looks like high values of the range get some weird behavior where the window binds itself.

I was trying to play with multiple windows as I think the z-index may have an effect here, but I couldn't get far test it with some of the above mentioned problems (not able to turn off snap and reposition windows too easily).

All comments based on viewing with IE7.

Nice work though. ;)

abe.elias
14 Dec 2008, 8:19 PM
Nice work David.

xantus
15 Dec 2008, 6:28 PM
There's a new version available! http://xant.us/ext-ux/examples/window-edge-snap/
(http://xant.us/ext-ux/examples/window-edge-snap/)
Changelog

v1.1: Added the viewport edges
v1.2: Rewrote the edge detection algorythm
v1.3: Increased response time of DropSnap on drag start
v1.4: Fixed bug where a window would snap to the incorrect area

xantus
16 Dec 2008, 11:11 AM
v1.6 is out! It will snap to the nearest window now, instead of the first window it was close enough to snap to.

Also note that I changed the the namespace in the source to match the advertised namespace.

Ext.ux.SnapWindow.DD -> Ext.ux.WindowSnap.DD

Animal
9 Feb 2009, 4:08 AM
Hey xantus, could you make use of these overrides I've been working on to enable my MouseProximityFade plugin to help with your proximity detection algorithm?

Pixel geometry is best delegated to the base lib's "Region" class so it doesn't complicate UI code.



Ext.override(Ext.lib.Region, {
/**
* Returns the shortest distance between this Region and another Region.
* Either or both Regions may be Points.
* @param {Region} r The other Region
* @return {Number} The shortest distance in pixels between the two Regions.
*/
getDistanceBetween: function(r) {

// We may need to mutate r, so make a copy.
r = Ext.apply({}, r);

// Translate r to the left of this
if (r.left > this.right) {
var rWidth = r.right - r.left;
r.left = this.left - (r.left - this.right) - rWidth;
r.right = r.left + rWidth;
}

// Translate r above this
if (r.top > this.bottom) {
var rHeight = r.bottom - r.top;
r.top = this.top - (r.top - this.bottom) - rHeight;
r.bottom = r.top + rHeight;
}

// If r is directly above
if (r.right > this.left) {
return this.top - r.bottom;
}

// If r is directly to the left
if (r.bottom > this.top) {
return this.left - r.right;
}

// r is on a diagonal path
return Math.round(Math.sqrt(Math.pow(this.top - r.bottom, 2) + Math.pow(this.left - r.right, 2)));
}
});

Ext.override(Ext.Element, {
/**
* Returns shortest distance between this Element and the specified point
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @return {Number} The shortest distance in pixels between this Element and the specified point.
*/
getDistanceTo: function(x, y) {
return this.getRegion().getDistanceBetween(new Ext.lib.Point(x, y));
},

/**
* Returns the shortest distance between this Element and another Element.
* @param {Element/DOMElement/String} el The other Element, or its ID.
* @return {Number} The shortest distance in pixels between the two Elements.
*/
getDistanceBetween: function(el) {
return this.getRegion().getDistanceBetween(Ext.fly(el).getRegion());
}
});

xantus
9 Feb 2009, 1:31 PM
Hey xantus, could you make use of these overrides I've been working on to enable my MouseProximityFade plugin to help with your proximity detection algorithm?

Pixel geometry is best delegated to the base lib's "Region" class so it doesn't complicate UI code.



Ext.override(Ext.lib.Region, {
/**
* Returns the shortest distance between this Region and another Region.
* Either or both Regions may be Points.
* @param {Region} r The other Region
* @return {Number} The shortest distance in pixels between the two Regions.
*/
getDistanceBetween: function(r) {

// We may need to mutate r, so make a copy.
r = Ext.apply({}, r);

// Translate r to the left of this
if (r.left > this.right) {
var rWidth = r.right - r.left;
r.left = this.left - (r.left - this.right) - rWidth;
r.right = r.left + rWidth;
}

// Translate r above this
if (r.top > this.bottom) {
var rHeight = r.bottom - r.top;
r.top = this.top - (r.top - this.bottom) - rHeight;
r.bottom = r.top + rHeight;
}

// If r is directly above
if (r.right > this.left) {
return this.top - r.bottom;
}

// If r is directly to the left
if (r.bottom > this.top) {
return this.left - r.right;
}

// r is on a diagonal path
return Math.round(Math.sqrt(Math.pow(this.top - r.bottom, 2) + Math.pow(this.left - r.right, 2)));
}
});

Ext.override(Ext.Element, {
/**
* Returns shortest distance between this Element and the specified point
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @return {Number} The shortest distance in pixels between this Element and the specified point.
*/
getDistanceTo: function(x, y) {
return this.getRegion().getDistanceBetween(new Ext.lib.Point(x, y));
},

/**
* Returns the shortest distance between this Element and another Element.
* @param {Element/DOMElement/String} el The other Element, or its ID.
* @return {Number} The shortest distance in pixels between the two Elements.
*/
getDistanceBetween: function(el) {
return this.getRegion().getDistanceBetween(Ext.fly(el).getRegion());
}
});


Thanks! I could use this next time I revisit that code, or if you'd like to tinker with it yourself?

wm003
10 Feb 2009, 12:52 AM
B) Very nice widget!

Animal
10 Feb 2009, 1:09 AM
The sample http://xant.us/ext-ux/examples/window-edge-snap/ does not appear to work any more. There's no snapping behaviour for me on FF 3.0.6

xantus
10 Feb 2009, 12:24 PM
It's working here on every browser I have: FF 3.0.5, Chrome, IE6, IE7, IE8. Could it be something in FF 3.0.6?



The sample http://xant.us/ext-ux/examples/window-edge-snap/ does not appear to work any more. There's no snapping behaviour for me on FF 3.0.6

mjlecomte
10 Feb 2009, 12:30 PM
Working for me FF3.0.6/FB 1.3X.2/Windows XP/

Animal
11 Feb 2009, 2:42 AM
Am I using it wrong?

I show two Windowas by clicking the "Open Window" button.

The "DragSnap on" button is depressed.

But there's no snapping behaviour. The Windows don't jump towards each other like magnets as soon as they get within 20px of each other. On all the browser I have, FF, Chrom, IE and Opera.

xantus
11 Feb 2009, 3:12 AM
Sorry, It should say DropSnap on. I just fixed the wording. With it on, you drop the window within 20px and it will attract to the nearest window edge. If you turn DropSnap off, then it will attract while you drag.

Hope that helps


Am I using it wrong?

I show two Windowas by clicking the "Open Window" button.

The "DragSnap on" button is depressed.

But there's no snapping behaviour. The Windows don't jump towards each other like magnets as soon as they get within 20px of each other. On all the browser I have, FF, Chrom, IE and Opera.

Animal
11 Feb 2009, 3:16 AM
Ah, got it! Thanks. I'll take a look at how it's wired up and see if any of the Region geometry methods can help to simplify it.

Animal
11 Feb 2009, 5:56 AM
Hi xantus,

Try the following code in place of window-edge-snap.js. It uses my addition to the Region class, and seems to be simpler, and work just about the same...



Ext.override(Ext.lib.Region, {
/**
* Returns the shortest distance between this Region and another Region.
* Either or both Regions may be Points.
* @param {Region} r The other Region
* @return {Number} The shortest distance in pixels between the two Regions.
*/
getDistanceBetween: function(r) {

// We may need to mutate r, so make a copy.
r = Ext.apply({}, r);

// Translate r to the left of this
if (r.left > this.right) {
var rWidth = r.right - r.left;
r.left = this.left - (r.left - this.right) - rWidth;
r.right = r.left + rWidth;
}

// Translate r above this
if (r.top > this.bottom) {
var rHeight = r.bottom - r.top;
r.top = this.top - (r.top - this.bottom) - rHeight;
r.bottom = r.top + rHeight;
}

// If r is directly above
if (r.right > this.left) {
return this.top - r.bottom;
}

// If r is directly to the left
if (r.bottom > this.top) {
return this.left - r.right;
}

// r is on a diagonal path
return Math.round(Math.sqrt(Math.pow(this.top - r.bottom, 2) + Math.pow(this.left - r.right, 2)));
}
});

/* Window Edge Snapping for Extjs
* Version: 1.6
*
* Copyright (c) 2008-2009 - David W Davis, All Rights Reserved
*
* xantus@xantus.org
* http://xant.us/
# http://extjs.com/forum/showthread.php?t=55213
*
* License: Same as Extjs 2.0
*
* Please do not remove this header
*/

Ext.namespace( 'Ext.ux.WindowSnap' );

// either create your own subclass and extend, or override Ext.Window directly
Ext.override( Ext.Window, {
initDraggable: function() {
this.dd = new Ext.ux.WindowSnap.DD(this);
}
});

// eventually these options should be taken from the window object
// but since Ext.Window does not pass a config obj to Ext.Window.DD
// we'll just set them here
Ext.ux.WindowSnap = {
version: '1.6',
snapRange: 20, // px
dragSnap: false,
dropSnap: true,
animateDropSnap: true
};

Ext.ux.WindowSnap.DD = function() {
Ext.ux.WindowSnap.DD.superclass.constructor.apply(this,arguments);
};

Ext.extend( Ext.ux.WindowSnap.DD, Ext.Window.DD, {

startDrag: function() {
Ext.ux.WindowSnap.DD.superclass.startDrag.apply(this,arguments);
if ( Ext.ux.WindowSnap.dragSnap ) {
this._getSnapData();
}
},

endDrag: function() {
Ext.ux.WindowSnap.DD.superclass.endDrag.apply(this,arguments);
if ( Ext.ux.WindowSnap.dropSnap ) {
this._getSnapData();
var pos = this.win.getPosition();
this.setSnapXY( this.win.el, pos[0], pos[1], true );
}
this.snapDD = [];
},

alignElWithMouse: function(el, iPageX, iPageY) {
var oCoord = this.getTargetCoord(iPageX, iPageY);
var fly = el.dom ? el : Ext.fly(el, '_dd');
if (!this.deltaSetXY) {
var aCoord = [oCoord.x, oCoord.y];
fly.setXY(aCoord);
var newLeft = fly.getLeft(true);
var newTop = fly.getTop(true);
this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
} else {
if ( Ext.ux.WindowSnap.dragSnap ) {
this.setSnapXY( fly, oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1] );
} else {
fly.setLeftTop( oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1] );
}
}

this.cachePosition(oCoord.x, oCoord.y);
this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
return oCoord;
},

setSnapXY: function( fly, x, y, drop ) {
var range = Ext.ux.WindowSnap.snapRange;

// Calculate the drag/drop Window's Region.
var r = drop ? this.win.el.getRegion() : new Ext.lib.Region(y, x + this.win.el.getWidth, y + this.win.el.getHeight(), x);

// Find the nearest other Window
var nearestDistance = Number.MAX_VALUE;
var nearest;
for ( var i = 0, len = this.snapDD.length; i < len; i++ ) {
var distance = r.getDistanceBetween(this.snapDD[i]);
if (distance < nearestDistance) {
nearest = this.snapDD[i];
nearestDistance = distance;
}
}

// If its within the range, snap to it.
if (nearestDistance <= range) {
if (r.right < nearest.left) {
x = nearest.left - this.win.el.getWidth();
} else if (r.left > nearest.right) {
x = nearest.right;
}
if (r.bottom < nearest.top) {
y = nearest.top - this.win.el.getHeight();
} else if (r.top > nearest.bottom) {
y = nearest.bottom;
}
}

// slide the window to the edge or just snap it
if ( drop && Ext.ux.WindowSnap.animateDropSnap ) {
fly.moveTo( x, y, { easing: 'bounceOut', duration: .3 } );
} else {
fly.setLeftTop( x, y );
}
},

_getSnapData: function() {
this.snapDD = [];
var win = this.win;
win.manager.each(function(w) {
if (w && w.isVisible() && (win !== w)) {
this.snapDD.push( w.el.getRegion() );
}
}, this);
}
});

mitchellsimoens
15 Jun 2010, 8:19 AM
I know it's been over a year since the last post and there is a new ExtJS version but I can't seem to get this working on ExtJS 3.2.1. Should it?

xantus
16 Jun 2010, 11:43 AM
You can try my other version:

http://github.com/xantus/comet-desktop/blob/master/public/desktop/lib/window-edge-snap.js


I know it's been over a year since the last post and there is a new ExtJS version but I can't seem to get this working on ExtJS 3.2.1. Should it?