PDA

View Full Version : Confine popup to layout-region; center it in same



Mike Robinson
3 Nov 2009, 4:33 PM
I have "the usual layout" of windows in a Viewport, and now I want to display a popup window. But I want this window to appear "centered" on the page, based on its actual height (which is AutoHeight: true).

Right now, the form appears more-or-less in the center of the browser window but quickly grows so that it exceeds the bottom margin. When I slide it up so that everything fits, it can slip "behind" (z-order issue...) the North panel, which at the moment consists of a hard-coded HTML definition ("panel.contentEl").

I'm open to suggestions. 8-|

Animal
4 Nov 2009, 12:30 AM
Render the Window to the region's layout target.

Mike Robinson
5 Nov 2009, 7:06 AM
Okay, I now see Panel.getLayoutTarget().

I see a bit of code in Saki's RecordForm plugin that I wonder about ... a bit of code in "getPanel" that refers to a this.formCt property and (this is why I bring it up...) does a renderTo under certain conditions. Is this something "standard," or is it a bit that Saki's doing?

Now, for the remaining question: the centering of a form that (somewhat to my surprise) grows into its present size as it appears, and, in the process of doing so, goes off the bottom edge of (gets clipped to...) the browser window? Is this the warned-about issue with "autoHeight?" Otherwise, how can I reasonably specify the dimensions that the form "ought to be" without actually counting-pixels at design time?

(Why am I using that? Simple: because Saki did it first. I don't claim to know quite what I am doing.) :)

Animal
5 Nov 2009, 7:12 AM
You may find this useful



Ext.override(Ext.BoxComponent, {
/**
* Synchronizes the height of this Container to exactly fit the specified descendant Component's content
* height with no scrollbars if possible, taking into account Panel and Window framing elements.
* The height calculated is constrained to either the passed maximum value, or the height of the browser viewport.
* @param {Component} c (Optional - defaults to this Container) The Component who's content height to sync to.
* @param {Number} min (Optional - defaults to 40) The minimum height to set this Container to.
* @param {Number} max (Optional - defaults to the browser viewport height) The maximum height to set this Container to.
*/
syncContentHeight: function(c, min, max) {
c = c || this;
min = min || 40;
max = max || Ext.lib.Dom.getViewportHeight();
var el = c.getContentTarget ? c.getContentTarget() : c.getEl();
el.setStyle('height', 'auto');
for (var h = el.dom.offsetHeight; c && c !== this.ownerCt; c = c.ownerCt) {
if (c.getFrameHeight) {
h += c.getFrameHeight();
}
}
this.setHeight(Math.max(Math.min(h, max), min)).doConstrain && this.doConstrain();
}
});


then



myWindow.synContentHeight(theFormPanelThatTheWindowContains);

Mike Robinson
5 Nov 2009, 7:29 AM
"Man, they don't call you 'The Animal' for nuthin'!" :D (he said, tipping his hat ... and picking-up his jaw from the floor).

I've defined a company-specific component class which we will use for "all pop-up windows of this sort." I can incorporate this functionality into it.

Is there a "generally accepted best-practice" for defining this kind of override to ExtJS-provided core classes (such as BoxComponent?). I've been reluctant to build extensions to the provided class-definitions, frankly as a hedge against future incompatibilities whenever "official" version x.y appears. ("Say... what am I thinking? By then, I'll be a consultant and they can pay me to fix it!" >:) ) ;)

Animal
5 Nov 2009, 7:31 AM
include an ExtOverrides.js file after ext-all.js containing the overrides (And fixes which they haven't yet fixed!)

Mike Robinson
5 Nov 2009, 7:46 AM
It always pays to have a bag o' tricks. Thanks again.

Mike Robinson
5 Nov 2009, 8:14 AM
Here's a follow-up question:



Am I correct that this should supplant the need to use autoHeight?
For the purposes of a pop-up panel that is initially hidden, should I specify forceLayout in the Form configuration? (It seems to me, if only intuitively, that the layout process is needed in order to obtain getFrameHeight().) Am I barking up a tree here?

Animal
5 Nov 2009, 8:22 AM
Don't use autoHeight.

You shouldn't need to specify forceLayout.

Mike Robinson
5 Nov 2009, 8:43 AM
In my experiments (watching carefully with FireBug), the loop calculates (h = 76) which obviously is not the required height of a form that contains any fields at all.

The resulting height (without autoHeight) is, in fact, exactly the size of the "OK" button-bar at the bottom of the form. Hence, it would appear to me that the height of the form's contents (the fields) is not being considered.

I have not yet delved into the guts of this thing :) deeply enough to know what "el.dom.offsetHeight" is meant to do. Should Ext.Element.getComputedHeight() be used? Should Ext.Panel.getInnerHeight(), or simply Ext.boxComponent.getHeight(), be used at some point?

I have found that I need to use render() in order to have a value for "c.getEl()."

Basically, what I'm trying to do is to achieve an "auto-height" effect without using that forbidden-property. I don't yet know the details of the framework well-enough to know precisely when, how, or even if the "automatic height" of the form's body ... being, "sufficient pixels to enclose all of the form elements" ... would be determined. I am intending that "doLayout()" would do so, if applied recursively.

My code now reads:
this.window.doLayout(false, true); // deep (recursive), even-if-hidden
this.window.render(this_app.getPopupWindowTarget()); // my function...
this.window.syncContentHeight(this.form); ... and I am right now pretty confident that I am missing something here. I don't want to have to explicitly specify the initial width and height of either the popup window or the form. I do have ample opportunity to calculate it on-the-fly, before displaying anything. But... how.

Edit: the "doLayout()" calls(s) above clearly do not do anything useful; nor does fiddling with el.getSize().height. The answer is still "76." The form is displayed exactly high-enough to contain the button-bar. However, the form can subsequently be expanded (by hand...) to reveal that all of the form-elements in fact are in the window.

Animal
5 Nov 2009, 10:44 AM
Configure your window with



listeners: {
afterlayout: function(w) {
w.syncContentHeight(theForm);
},
single: true
}


So that it runs after the contents of the Window have fully rendered.

Mike Robinson
5 Nov 2009, 12:21 PM
The result is still 76!

Here's what I'm seeing:


Until the form is displayed, all of the "form.items.items[]" entries show that they are not yet "rendered." The getSize() method for each one of them returns nothing. (As in: the FireBug monitor displays nothing-at-all in the console.)
At the time that the event you suggested fires, this is the state-of-things, and the result is therefore still 76.
Typing things like this.form.doLayout(false, true) doesn't change anything...
When the form has been displayed once, "this.form.items.items[0].getSize()" when entered in the console now returns a more-plausible value such as: "Object width=400, height=22." (this.form.getEl().dom.offsetHeight returns the same value.)
However... this.form.getSize() returns more-or-less the same value it always did, e.g. "Object width=588 height=51". It does not equal the sum of the height of the items.

In short, it seems to me that somehow the crux of the problem is that ... "lazy ol' ExtJS!!" ... we don't know how large the form is going to be until we display it. "doLayout()" doesn't seem to do it, and neither does "render()" on the window.

Until this happens, we don't know the size of the form-fields. Which is what we need to know at the time your code runs. And I haven't yet found anything that returns "the size of the bounding-rectangle encompassing this form's body-area ... all of this form's fields as they will appear when this form is displayed."

I graciously appreciate your continued help, Animal.

Animal
5 Nov 2009, 12:34 PM
It's not hidden using display in a card layout is it? Or umrendered due to being collapsed etc?

Because this works just fine, and expands the Window to perfectly fit the FormPanel:



var myForm = new Ext.form.FormPanel({
border: false,
bodyStyle: {
padding: '5px'
},
defaultType: 'textfield',
items: [{
fieldLabel: 'Textfield 1'
},{
fieldLabel: 'Textfield 2'
},{
fieldLabel: 'Textfield 3'
},{
fieldLabel: 'Textfield 4'
},{
fieldLabel: 'Textfield 5'
},{
fieldLabel: 'Textfield 6'
}]
});
new Ext.Window({
width: 500,
height: 10, // Start off ridiculously small...
closeAction:'hide',
listeners: {
afterlayout: function(w) {
w.syncContentHeight(myForm);
}
},
items: myForm,
buttons: [{
text:'Submit',
disabled:true
},{
text: 'Close',
handler: function(){
win.hide();
}
}]
}).show();

Mike Robinson
5 Nov 2009, 12:59 PM
:-? ... hmm, I wonder ...


Well... looky... at ... what ... works ... differently ... in ... FireFox ... versus ... IE.In Firefox, I get the results that I have been describing.

In IE, I get a window that is expanded.

(Gosh, I guess I'm just used to IE being the one that screws-up ...)

Now what? :-|

Animal
6 Nov 2009, 12:11 AM
I only use Firefox.

They make me port code to IE at work (at great expense... probably another 20% added development time to overcome IE's crapness), but by default, I keep it out of sight and out of mind.