PDA

View Full Version : Center a window before show



Dave.Sanders
6 Aug 2009, 10:07 AM
This is a really tiny, petty thing, but I figured I'd ask anyway:

I have a window that I'm using as a dialog box / form that pops up for the user. Because of how my page is set up (ViewPort -> Center Panel is border layout, etc.) the automatic showing of the window centers it above the panel that spawns it, but not in the middle of the viewport like it is expected.

No big deal, I can do a myWindow.show() and a myWindow.center() which works - BUT, it seems that on some dialogs (and I haven't figure out why only on some of them) the window "bounces" from the original center to the new center. It all still works, but that extra little hop looks bad and unprofessional.

Is there an easy way to center the dialog BEFORE the show so I eliminate the jump of the window? Keep in mind that the window hasn't been rendered yet - so els don't exist yet. (if you put center before show, you get an error) Maybe I can render it somehow invisibly, center it, then show it?

I'm sure someone else has solved this already.
thx
Dave

6 Aug 2009, 12:21 PM
If you know the height/width of the window before render time, you can calculate the dimensions of the viewport (plus offsets) and derive the perfect xy coordinates for the window to know upon instantiation.

Dave.Sanders
7 Aug 2009, 10:11 AM
Actually, it will work without a center() at all, IF you specify the width and height first.

Based on my testing, it looks like if you don't specify a width and height, it tries to center it before the show, but it has the wrong width and height specified on the component. (Since it doesn't know what it is yet I imagine.)

Once I gave it some width and height params, the window.show() automatically centers it for you.

jasonb885
9 Aug 2009, 1:18 PM
I encountered the same issue myself. Prior to ExtJS 3, it was possible to have a FormPanel as an item of an Ext.Window, specifying no height for anything, and ExtJS 2.2.2 would correctly center the window anyway. Now, it's centered as if the window had no contents.

Haven't come up with a solution I am happy with thus far.

I could set height, but I don't know what it will be as the form items vary dynamically.

Anyone have any ideas?



new Ext.Viewport({
layout:'fit',
items:[
new Ext.Panel({
tbar:[
new Ext.Button({
text:'a button that is easy to click',
listeners:{
'click':{
fn:function() {
if(!this._window) {
this._window = new Ext.Window({
width:400,
autoHeight:true,
title:'test',
closeAction:'hide',
modal:true,
layout:'auto',
items:[
{
autoScroll:true,
defaultType:'textfield',
xtype:'form',
items:personEditItems
}
]
});
this._window.on('show', function(w) {
w.center();
});
}
this._window.show();
//form.show();
}
}
}
})
]
})
]
});

var miniItems = [
{
name:'person[first]',
fieldLabel:'Name',
width:75,
requiredField:true,
helpText:'First',
inlineItems:[
{
helpText:'Middle',
name:'person[middle]',
width:75,
xtype:'textfield'
},
{
helpText:'Last',
name:'person[last]',
width:75,
requiredField:true,
xtype:'textfield'
}]
}
];

Animal
9 Aug 2009, 1:35 PM
I can't make it fail here with



new Ext.Window({
width:400,
autoHeight:true,
title:'test',
closeAction:'hide',
modal:true,
layout:'auto',
items:[{
autoScroll:true,
defaultType:'textfield',
xtype:'form',
defaults: {anchor: '95%'},
items:[
{fieldLabel: 'Test1'},
{fieldLabel: 'Test2'},
{fieldLabel: 'Test3'}
]
}]
}).show();

jasonb885
9 Aug 2009, 2:12 PM
Broke it down to the lowest common denominator. At least on FireFox 3.0.11 with fb running, the call to w.center() repositions the window to the center of my browser window. Same behavior in Opera 10 beta 2.

Not sure what I should be doing differently.



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>Liaison</title>
<link href="/css/ext-all.css" media="all" rel="stylesheet" type="text/css" />
<script src="/js/liaison-ui/extjs/ext-base-debug.js" type="text/javascript"></script>
<script src="/js/liaison-ui/extjs/ext-all-debug.js" type="text/javascript"></script>
<script>
Ext.onReady(function(){

var w = new Ext.Window({
width:400,
autoHeight:true,
title:'test',
closeAction:'hide',
modal:true,
layout:'auto',
defaults: {anchor: '95%'},
items:[{
autoScroll:true,
defaultType:'textfield',
xtype:'form',
items:[
{fieldLabel: 'Test1'},
{fieldLabel: 'Test2'},
{fieldLabel: 'Test3'},
{fieldLabel: 'Test1'},
{fieldLabel: 'Test2'},
{fieldLabel: 'Test3'},
{fieldLabel: 'Test1'},
{fieldLabel: 'Test2'},
{fieldLabel: 'Test3'}
]
}]
}).show();
(function(){w.center()}).defer(250);
});
</script>
</head>
<body>
</body>
</html>

Animal
9 Aug 2009, 9:32 PM
Try rendering it to the Element in which you want it centered.

jasonb885
10 Aug 2009, 5:37 AM
Try rendering it to the Element in which you want it centered.

My stock ExtJS 3 behaves differently from yours?

I want it centered inside Ext.getBody(). Is that renderTo:Ext.getBody()? I've never needed renderTo before, is that what it's for?

Thanks.

Animal
10 Aug 2009, 5:41 AM
That's where it's rendered by default.

I think this could be a bug which is being processed right now to do with layouting of initially hidden uncontained Containers (which is what Window is).

They don't get layed out on render, and they should. If there is no ownerCt at afterRender time, then a Container must lay itself out. But if its DOM element is hidden, it does not do this.

jasonb885
10 Aug 2009, 5:48 AM
That's where it's rendered by default.

I think this could be a bug which is being processed right now to do with layouting of initially hidden uncontained Containers (which is what Window is).

They don't get layed out on render, and they should. If there is no ownerCt at afterRender time, then a Container must lay itself out. But if its DOM element is hidden, it does not do this.

Interesting.

Any chance someone can post the fix once there is one as I do not have svn access?

Thank you!

Animal
10 Aug 2009, 6:58 AM
AFAIK, the fixes are



Ext.override(Ext.Container, {
bufferResize: false,

render: Ext.Component.prototype.render,

afterRender: function(){
Ext.Container.superclass.afterRender.call(this);
if(this.layout){
if(Ext.isObject(this.layout) && !this.layout.layout){
this.layoutConfig = this.layout;
this.layout = this.layoutConfig.type;
}
if(Ext.isString(this.layout)){
this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig);
}
this.setLayout(this.layout);
}
if(this.activeItem !== undefined){
var item = this.activeItem;
delete this.activeItem;
this.layout.setActiveItem(item);
}
if(!this.ownerCt){
// force a layout if no ownerCt is set
this.doLayout(false, true);
}
if(this.monitorResize === true){
Ext.EventManager.onWindowResize(this.doLayout, this, [false]);
}
},

targetHasLayout: function() {
var el = this.getVisibiltyEl();
return el && !el.isStyle("display", "none");
},

doLayout: function(shallow, force){
var rendered = this.rendered,
forceLayout = this.forceLayout;

if(!this.targetHasLayout() || this.collapsed){
this.deferLayout = this.deferLayout || !shallow;
if(!(force || forceLayout)){
return;
}
shallow = shallow && !this.deferLayout;
} else {
delete this.deferLayout;
}
if(rendered && this.layout){
this.layout.layout();
}
if(shallow !== true && this.items){
var cs = this.items.items;
for(var i = 0, len = cs.length; i < len; i++){
var c = cs[i];
if(c.doLayout){
c.forceLayout = forceLayout;
c.doLayout();
}
}
}
if(rendered){
this.onLayout(shallow, force);
}
delete this.forceLayout;
}
});


and



Ext.override(Ext.Window, {
initHidden : false,
hidden : true,

afterShow : function(isAnim){
this.proxy.hide();
this.el.setStyle('display', 'block');
this.el.show();
if(this.maximized){
this.fitContainer();
}
if(Ext.isMac && Ext.isGecko){ // work around stupid FF 2.0/Mac scroll bar bug
this.cascade(this.setAutoScroll);
}

if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
Ext.EventManager.onWindowResize(this.onWindowResize, this);
}
this.doConstrain();
if(this.keyMap){
this.keyMap.enable();
}
this.toFront();
this.updateHandles();
if(isAnim && (Ext.isIE || Ext.isWebKit)){
var sz = this.getSize();
this.onResize(sz.width, sz.height);
}
this.fireEvent('show', this);
}
});

jasonb885
10 Aug 2009, 12:26 PM
Thanks!

jasonb885
12 Aug 2009, 4:36 PM
After applying the above, a call to new Ext.Window produces a window that is already rendered:true, disallowing me from usefully attaching events to render or afterlayout passed in via a listeners config.



... inside a class that manages a window ...
this.window = new Ext.Window({
height:300,
width:300,
items:[{xtype:'some-preconfigured-form'}],
listeners:{
afterlayout:{
scope:this,
fn:function() {
var f = this.form.getForm();
f.waitMsgTarget = f.getEl();
}
}
}
});
this.form = this.window.form;


In this case, this.form is assigned from this.window.form back reference. However, the form renders immediately and the next line never executes.

While I'm sure I can work around this particular issue, isn't Ext.Window supposed to not be rendered until you call show() if initHidden is true, its default?

The smallest test case is simply loading ExtJS, applying the earlier overrides, then calling new Ext.Window() from the fb console. The property rendered is true. Doing the same thing from the fb console for the ExtJS API page or without the overrides applied produces an Ext.Window with rendered:false, the expected outcome.

Animal
12 Aug 2009, 9:42 PM
Can't repro that here:

http://i131.photobucket.com/albums/p286/TimeTrialAnimal/notrendered.jpg

jasonb885
13 Aug 2009, 2:07 PM
For me, it produces a rendered window in Firefox 3/Linux, Konqueror 4.2.2, and Opera 10 beta 2.



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>Liaison</title>
<link href="/css/ext-all.css" media="all" rel="stylesheet" type="text/css" />
<script src="/js/liaison-ui/extjs/ext-base-debug.js" type="text/javascript"></script>
<script src="/js/liaison-ui/extjs/ext-all-debug.js" type="text/javascript"></script>
<script>
Ext.override(Ext.Container, {
bufferResize: false,

render: Ext.Component.prototype.render,

afterRender: function(){
Ext.Container.superclass.afterRender.call(this);
if(this.layout){
if(Ext.isObject(this.layout) && !this.layout.layout){
this.layoutConfig = this.layout;
this.layout = this.layoutConfig.type;
}
if(Ext.isString(this.layout)){
this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig);
}
this.setLayout(this.layout);
}
if(this.activeItem !== undefined){
var item = this.activeItem;
delete this.activeItem;
this.layout.setActiveItem(item);
}
if(!this.ownerCt){
// force a layout if no ownerCt is set
this.doLayout(false, true);
}
if(this.monitorResize === true){
Ext.EventManager.onWindowResize(this.doLayout, this, [false]);
}
},

targetHasLayout: function() {
var el = this.getVisibiltyEl();
return el && !el.isStyle("display", "none");
},

doLayout: function(shallow, force){
var rendered = this.rendered,
forceLayout = this.forceLayout;

if(!this.targetHasLayout() || this.collapsed){
this.deferLayout = this.deferLayout || !shallow;
if(!(force || forceLayout)){
return;
}
shallow = shallow && !this.deferLayout;
} else {
delete this.deferLayout;
}
if(rendered && this.layout){
this.layout.layout();
}
if(shallow !== true && this.items){
var cs = this.items.items;
for(var i = 0, len = cs.length; i < len; i++){
var c = cs[i];
if(c.doLayout){
c.forceLayout = forceLayout;
c.doLayout();
}
}
}
if(rendered){
this.onLayout(shallow, force);
}
delete this.forceLayout;
}
});

Ext.override(Ext.Window, {
initHidden : false,
hidden : true,

afterShow : function(isAnim){
this.proxy.hide();
this.el.setStyle('display', 'block');
this.el.show();
if(this.maximized){
this.fitContainer();
}
if(Ext.isMac && Ext.isGecko){ // work around stupid FF 2.0/Mac scroll bar bug
this.cascade(this.setAutoScroll);
}

if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
Ext.EventManager.onWindowResize(this.onWindowResize, this);
}
this.doConstrain();
if(this.keyMap){
this.keyMap.enable();
}
this.toFront();
this.updateHandles();
if(isAnim && (Ext.isIE || Ext.isWebKit)){
var sz = this.getSize();
this.onResize(sz.width, sz.height);
}
this.fireEvent('show', this);
}
});
Ext.onReady(function() {
var w = new Ext.Window({});
console.log(w.rendered);
});
</script>
</head>
<body>
</body>
</html>

jasonb885
14 Aug 2009, 6:23 AM
Ext.override(Ext.Window, {
initHidden : false,
hidden : true,


Toggling the value of initHidden changes my behavior for rendered to the opposite of its set value above.



initHidden : Boolean
True to hide the window until show() is explicitly called (defaults to true).


While I don't know what specifically is causing my behavior on a vanilla ExtJS with only the above overrides applied, changing the default for initHidden does appear to directly change the behavior of a new, instantiated instance of Ext.Window.

Commenting out those values doesn't seem to alter the effectiveness of the overrides with respect to centering a window.

Animal
14 Aug 2009, 7:20 AM
This anomaly has been addressed too.

initHidden has been deprecated in new code (Neither I nor Even knew why it had been added when it basically did the same thing as the hidden config).

The hidden config is used by Window now as it should be.

Laurent Mignon
3 Sep 2009, 1:05 AM
Hi,

Once your patch is applied, if I create a MessageBox, it's rendered without its title... :-/

If write:



var dlg = Ext.Msg.show({title: 'mytitle', ....});
dlg.getDialog().setTitle('mytitle');
The title is correctly displayed.

Laurent Mignon

jasonb885
22 Sep 2009, 4:01 PM
What's more, it seems to break *box layouts inside an Ext.Window. After a manual resize, the layout works correctly.

I wonder what form this fix will take in the next generally available release of 3.x?

Animal
22 Sep 2009, 11:06 PM
Can you point to an online example that fails?

jasonb885
23 Sep 2009, 3:31 PM
Can you point to an online example that fails?

Not online. Locally with the above override applied, I have to move this window for the *box layout to render correctly.



var v = new Ext.Window({
height:300,
width:300,
layout:'hbox',
layoutConfig:{align:'stretch'},
items:[
{flex:1, xtype:'panel', layout:'form', items:[{xtype:'textfield', fieldLabel:'f1'}]},
{flex:1, xtype:'panel', layout:'form', items:[{xtype:'textfield', fieldLabel:'f2'}]}
]
});
v.show();


If I disable the override, it works just fine.