PDA

View Full Version : Cascading Windows



bpjohnson
3 Mar 2008, 1:22 PM
[Edit: Animal offered a very nice riff on the same theme further down (http://extjs.com/forum/showthread.php?p=133775#post133775) in this thread.]

Ever notice how, by default, new Ext Windows are created in the center of their containing element? I thought it'd be nice to have the first window start at the top left, and each subsequent window start down and to the right a bit, like in most OSes. Hence this plugin.

To call a window that will cascade:



win = new Ext.Window({
layout:'fit',
width:500,
height:300,
closeAction:'hide',
plain: true,
plugins: [ new Ext.ux.plugins.CascadeWindows() ],
offSetX: 15,
offSetY: -15,
items: [{title: 'Hello World', html: 'lorem ipsum'}],
});

win.show();
The offSetX and offSetY options set how far down and over each window is created. If you don't specify them, the defaults are 15px. Oh, and I should note that cascading is relative to the window's windowgroup, defaulting to the global Ext.WindowMgr if one hasn't been specified.

And here's the plugin code:


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

Ext.ux.plugins.CascadeWindows = function(config) {
Ext.apply(this, config);
};

Ext.extend(Ext.ux.plugins.CascadeWindows, Ext.util.Observable, {
init:function(win) {
Ext.apply(win, {
onRender:win.onRender.createSequence(function(ct, position) {
var mgr = (this.manager)?this.manager:Ext.WindowMgr;
var ctr = this.container;
var size = { width: this.width,
height: this.height };
var ctSize = this.getEl().getAlignToXY(ctr.id, "tl-br");
var offSetX = (this.offSetX)?this.offSetX:15;
var offSetY = (this.offsetY)?this.offsetY:-15;
if (mgr.cascaded) {
mgr.cascaded += 1;
} else {
mgr.cascaded = 1;
}
if (mgr.cascaded == 1) {
mgr.lastXY = this.getEl().getAlignToXY(ctr.id, "tl-tl?", [offSetX,offSetY]);
} else {
mgr.lastXY[0] += 40;
mgr.lastXY[1] += 25;
var testBR = {bottom: (size.height + mgr.lastXY[1]),
right: (size.width + mgr.lastXY[0]) };
if (testBR.bottom > ctSize[1]) {
mgr.lastXY[1] = 0;
}
if (testBR.right > ctSize[0]) {
mgr.lastXY[0] = 15;
}
}
this.cascaded = true;
this.origPos = [];
this.origPos[0] = mgr.lastXY[0];
this.origPos[1] = mgr.lastXY[1];
this.setPosition(mgr.lastXY[0], mgr.lastXY[1]);
}),
cascade: function(e) {
this.setPosition(this.origPos[0], this.origPos[1]);
}
});
} // end of function init
}); // end of extend

franckxx
3 Mar 2008, 2:30 PM
Hi bpjohnson,

It's interesting, have your got online demo ?
it's can be usefull, thx !

zzo
3 Mar 2008, 11:15 PM
Works great with the Desktop app... just added:



,plugins: [ new Ext.ux.plugins.CascadeWindows() ]


to createWindow in Desktop.js

thanks!
Mark

PS Note it's NOT 'CascadingWindows' ...

Animal
4 Mar 2008, 1:05 AM
This adds a config option cascadeOnFirstShow which is a pixel offset to cascade any new Window down and right from it's previous sibling:



Ext.override(Ext.Window, {
beforeShow : function(){
delete this.el.lastXY;
delete this.el.lastLT;
if(this.x === undefined || this.y === undefined){
var xy = this.el.getAlignToXY(this.container, 'c-c');
var pos = this.el.translatePoints(xy[0], xy[1]);
this.x = this.x === undefined? pos.left : this.x;
this.y = this.y === undefined? pos.top : this.y;
if (this.cascadeOnFirstShow) {
var prev;
this.manager.each(function(w) {
if (w == this) {
if (prev) {
var o = (typeof this.cascadeOnFirstShow == 'number') ? this.cascadeOnFirstShow : 20;
var p = prev.getPosition();
this.x = p[0] + o;
this.y = p[1] + o;
}
return false;
}
if (w.isVisible()) prev = w;
}, this);
}
}
this.el.setLeftTop(this.x, this.y);

if(this.expandOnShow){
this.expand(false);
}

if(this.modal){
Ext.getBody().addClass("x-body-masked");
this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
this.mask.show();
}
}
});

bpjohnson
4 Mar 2008, 8:18 AM
Works great with the Desktop app... just added:



,plugins: [ new Ext.ux.plugins.CascadeWindows() ]
to createWindow in Desktop.js

thanks!
Mark

PS Note it's NOT 'CascadingWindows' ...

Thanks for pointing that out. I guess I should proofread after coffee and not before. :) I've fixed the example in the first post.

bpjohnson
4 Mar 2008, 8:22 AM
This adds a config option cascadeOnFirstShow which is a pixel offset to cascade any new Window down and right from it's previous sibling:



Okay, that's pretty dang cool. Thanks for showing me a different and somewhat less BFI way of doing things.

I'm still a bit fuzzy on when to override versus extend or plug-in, so I guess I have some more reading to do...

B

jay@moduscreate.com
4 Mar 2008, 9:54 AM
Override replaces a method/property
Extend copies an existing object, extends and/or overrides methods/properties.

bpjohnson
4 Mar 2008, 10:17 AM
Override replaces a method/property
Extend copies an existing object, extends and/or overrides methods/properties.

Thanks for the info! That much I felt fairly clear on; my lack of clarity more revolves around when does one use an override versus an extend (it seems to me when you still want the original to work as advertised) and when does one use an override or extension versus a plug-in?

But it seems that this has been talked to death in other threads. If only some enterprising person would comb through the vast store of knowledge that is the forums and perhaps write an abstract for the wiki... Sadly I lack the gumption. :)

B

jay@moduscreate.com
4 Mar 2008, 4:27 PM
An override will apply to your code globally. Instead of an extension, where you have to do a search and replace in your code where you are using that class.

Good overrides will fix code and/or add to functionality w/out being cumbersome. Case in point, something i published the other day: Here ('http://tdg-i.com/22/gridview-override-adding-showall-button-and-columnviews-filter-sets') You can specify groups of visible columns in your code. If the columnViews object doesn't exist, it's as if the override never existed... with one exception, the added 'show all' button. ;)

Animal
5 Mar 2008, 8:01 AM
Here it is as a plugin:



Ext.ux.WindowCascade = Ext.extend(Object, {
constructor: function(offset) {
this.offset = offset;
},

init: function(client) {
client.beforeShow = Ext.Window.prototype.beforeShow.createInterceptor(this.beforeShow);
},

beforeShow: function() {
if ((this.x == undefined) && (this.y == undefined)) {
var prev;
this.manager.each(function(w) {
if (w == this) {
if (prev) {
var o = this.offset || 20;
var p = prev.getPosition();
this.x = p[0] + o;
this.y = p[1] + o;
}
return false;
}
if (w.isVisible()) prev = w;
}, this);
}
}
});

NBRed5
5 Mar 2008, 8:36 AM
Animal,

Have you tried using your cascade method on windows that are constrained to one particular region of a viewport. I does not work as getPosition returns the global page cordinates and not the coordinates within the constraining element.

Try the following:



var p = this.el.translatePoints(prev.getPosition()[0], prev.getPosition()[1]);;
this.x = p.left + o;
this.y = p.top + o;

NBRed5
5 Mar 2008, 8:45 AM
Animal,

Just read up from API docs for BoxComponent.getPosition and founfd the following:



getPosition( [Boolean local] ) : Array
Gets the current XY position of the component's underlying element.
Parameters:
local : Boolean
(optional) If true the element's left and top are returned instead of page XY (defaults to false)
Returns:
Array
The XY position of the element (e.g., [100, 200])


So instead of the code I posted below you could use getPosition(true) and it would work for constrained windows as well.

bpjohnson
5 Mar 2008, 8:51 AM
Animal,

Have you tried using your cascade method on windows that are constrained to one particular region of a viewport. I does not work as getPosition returns the global page cordinates and not the coordinates within the constraining element




Animal's plugin, while very elegant, also misses two features of my take on cascading that I seem to have failed to have previously mentioned:

If your windows bump the bottom of their container (or viewport), they'll start back over at the top, shifted a bit further right.

Also, if you call win.cascade() the window will return to it's original position. This could be used in something like this:


var cleanUpWindows = function (mgr) {
mgr = (mgr)?mgr:Ext.WindowMgr;
mgr.each(function(win) {
if(win.minimized || win.hidden){
win.show();
}
if (win.cascaded) {
win.cascade();
}
win.toFront();
});
};

Although neither would be difficult to re-implement using Animal's code.

Animal
5 Mar 2008, 9:07 AM
OK, that's a bit of extra functionality you have.

Really the only thing I added was use of the previous sibling window to calculate the new window position, so it cascaded from wherever the last window in the heirarchy happened to be placed. I think that's a neat idea.

bpjohnson
5 Mar 2008, 9:12 AM
OK, that's a bit of extra functionality you have.

Really the only thing I added was use of the previous sibling window to calculate the new window position, so it cascaded from wherever the last window in the heirarchy happened to be placed. I think that's a neat idea.

Don't get me wrong, I think that's a very neat idea, too! In fact, I plan to steal it, file the serial numbers off, and incorporate it into my own code the next chance I get. :D

It's a much cleaner way of getting the initial position than mine. I need to play with it a bit, though, see what happens in our use cases. I'm curious, if I move the current window, make a new window, it seems the cascade be based on the previous sibling's new position? Hmm... That could cause some strange clutter.

Animal
5 Mar 2008, 9:32 AM
OK, perhaps it should just cascade off the topmost visible Window's position? That's doable too with Ext.WindowMgr.getActive()

frew
16 Jun 2009, 9:30 AM
Just wanted to say that the original plugin works great with Ext 3 SVN.