/** * @class Ext.window.Window * @extends Ext.panel.Panel * <p>A specialized panel intended for use as an application window. Windows are floated, {@link #resizable}, and * {@link #draggable} by default. Windows can be {@link #maximizable maximized} to fill the viewport, * restored to their prior size, and can be {@link #minimize}d.</p> * <p>Windows can also be linked to a {@link Ext.ZIndexManager} or managed by the {@link Ext.WindowManager} to provide * grouping, activation, to front, to back and other application-specific behavior.</p> * <p>By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element * specify {@link Ext.Component#renderTo renderTo}.</p> * <p><b>As with all {@link Ext.container.Container Container}s, it is important to consider how you want the Window * to size and arrange any child Components. Choose an appropriate {@link #layout} configuration which lays out * child Components in the required manner.</b></p> * {@img Ext.window.Window/Ext.window.Window.png Window component} * Example:<code><pre> Ext.create('Ext.window.Window', { title: 'Hello', height: 200, width: 400, layout: 'fit', items: { // Let's put an empty grid in just to illustrate fit layout xtype: 'grid', border: false, columns: [{header: 'World'}], // One header just for show. There's no data, store: Ext.create('Ext.data.ArrayStore', {}) // A dummy empty data store } }).show(); </pre></code> * @constructor * @param {Object} config The config object * @xtype window */ Ext.define('Ext.window.Window', { extend: 'Ext.panel.Panel', alternateClassName: 'Ext.Window', requires: ['Ext.util.ComponentDragger', 'Ext.util.Region', 'Ext.EventManager'], alias: 'widget.window', /** * @cfg {Number} x * The X position of the left edge of the window on initial showing. Defaults to centering the Window within * the width of the Window's container {@link Ext.core.Element Element) (The Element that the Window is rendered to). */ /** * @cfg {Number} y * The Y position of the top edge of the window on initial showing. Defaults to centering the Window within * the height of the Window's container {@link Ext.core.Element Element) (The Element that the Window is rendered to). */ /** * @cfg {Boolean} modal * True to make the window modal and mask everything behind it when displayed, false to display it without * restricting access to other UI elements (defaults to false). */ /** * @cfg {String/Element} animateTarget * Id or element from which the window should animate while opening (defaults to null with no animation). */ /** * @cfg {String/Number/Component} defaultFocus * <p>Specifies a Component to receive focus when this Window is focused.</p> * <p>This may be one of:</p><div class="mdetail-params"><ul> * <li>The index of a footer Button.</li> * <li>The id or {@link Ext.AbstractComponent#itemId} of a descendant Component.</li> * <li>A Component.</li> * </ul></div> */ /** * @cfg {Function} onEsc * Allows override of the built-in processing for the escape key. Default action * is to close the Window (performing whatever action is specified in {@link #closeAction}. * To prevent the Window closing when the escape key is pressed, specify this as * Ext.emptyFn (See {@link Ext#emptyFn Ext.emptyFn}). */ /** * @cfg {Boolean} collapsed * True to render the window collapsed, false to render it expanded (defaults to false). Note that if * {@link #expandOnShow} is true (the default) it will override the <code>collapsed</code> config and the window * will always be expanded when shown. */ /** * @cfg {Boolean} maximized * True to initially display the window in a maximized state. (Defaults to false). */ /** * @cfg {String} baseCls * The base CSS class to apply to this panel's element (defaults to 'x-window'). */ baseCls: Ext.baseCSSPrefix + 'window', /** * @cfg {Mixed} resizable * <p>Specify as <code>true</code> to allow user resizing at each edge and corner of the window, false to disable * resizing (defaults to true).</p> * <p>This may also be specified as a config object to </p> */ resizable: true, /** * @cfg {Boolean} draggable * <p>True to allow the window to be dragged by the header bar, false to disable dragging (defaults to true). Note * that by default the window will be centered in the viewport, so if dragging is disabled the window may need * to be positioned programmatically after render (e.g., myWindow.setPosition(100, 100);).<p> */ draggable: true, /** * @cfg {Boolean} constrain * True to constrain the window within its containing element, false to allow it to fall outside of its * containing element. By default the window will be rendered to document.body. To render and constrain the * window within another element specify {@link #renderTo}. * (defaults to false). Optionally the header only can be constrained using {@link #constrainHeader}. */ constrain: false, /** * @cfg {Boolean} constrainHeader * True to constrain the window header within its containing element (allowing the window body to fall outside * of its containing element) or false to allow the header to fall outside its containing element (defaults to * false). Optionally the entire window can be constrained using {@link #constrain}. */ constrainHeader: false, /** * @cfg {Boolean} plain * True to render the window body with a transparent background so that it will blend into the framing * elements, false to add a lighter background color to visually highlight the body element and separate it * more distinctly from the surrounding frame (defaults to false). */ plain: false, /** * @cfg {Boolean} minimizable * True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button * and disallow minimizing the window (defaults to false). Note that this button provides no implementation -- * the behavior of minimizing a window is implementation-specific, so the minimize event must be handled and a * custom minimize behavior implemented for this option to be useful. */ minimizable: false, /** * @cfg {Boolean} maximizable * True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button * and disallow maximizing the window (defaults to false). Note that when a window is maximized, the tool button * will automatically change to a 'restore' button with the appropriate behavior already built-in that will * restore the window to its previous size. */ maximizable: false, // inherit docs minHeight: 100, // inherit docs minWidth: 200, /** * @cfg {Boolean} expandOnShow * True to always expand the window when it is displayed, false to keep it in its current state (which may be * {@link #collapsed}) when displayed (defaults to true). */ expandOnShow: true, // inherited docs, same default collapsible: false, /** * @cfg {Boolean} closable * <p>True to display the 'close' tool button and allow the user to close the window, false to * hide the button and disallow closing the window (defaults to <code>true</code>).</p> * <p>By default, when close is requested by either clicking the close button in the header * or pressing ESC when the Window has focus, the {@link #close} method will be called. This * will <i>{@link Ext.Component#destroy destroy}</i> the Window and its content meaning that * it may not be reused.</p> * <p>To make closing a Window <i>hide</i> the Window so that it may be reused, set * {@link #closeAction} to 'hide'.</p> */ closable: true, * @cfg {Boolean} hidden * Render this Window hidden (default is <code>true</code>). If <code>true</code>, the * {@link #hide} method will be called internally. */ hidden: true, // Inherit docs from Component. Windows render to the body on first show. autoRender: true, // Inherit docs from Component. Windows hide using visibility. hideMode: 'visibility', /** @cfg {Boolean} floating @hide Windows are always floating*/ floating: true, ariaRole: 'alertdialog', itemCls: 'x-window-item', overlapHeader: true, ignoreHeaderBorderManagement: true, // private initComponent: function() { var me = this; me.callParent(); me.addEvents( /** * @event activate * Fires after the window has been visually activated via {@link #setActive}. * @param {Ext.window.Window} this */ /** * @event deactivate * Fires after the window has been visually deactivated via {@link #setActive}. * @param {Ext.window.Window} this */ /** * @event resize * Fires after the window has been resized. * @param {Ext.window.Window} this * @param {Number} width The window's new width * @param {Number} height The window's new height */ 'resize', /** * @event maximize * Fires after the window has been maximized. * @param {Ext.window.Window} this */ 'maximize', /** * @event minimize * Fires after the window has been minimized. * @param {Ext.window.Window} this */ 'minimize', /** * @event restore * Fires after the window has been restored to its original size after being maximized. * @param {Ext.window.Window} this */ 'restore' ); if (me.plain) { me.addClsWithUI('plain'); } if (me.modal) { me.ariaRole = 'dialog'; } }, // State Management // private initStateEvents: function(){ var events = this.stateEvents; // push on stateEvents if they don't exist Ext.each(['maximize', 'restore', 'resize', 'dragend'], function(event){ if (Ext.Array.indexOf(events, event)) { events.push(event); } }); this.callParent(); }, getState: function() { var me = this, state = me.callParent() || {}, maximized = !!me.maximized; state.maximized = maximized; Ext.apply(state, { size: maximized ? me.restoreSize : me.getSize(), pos: maximized ? me.restorePos : me.getPosition() }); return state; }, applyState: function(state){ var me = this; if (state) { me.maximized = state.maximized; if (me.maximized) { me.hasSavedRestore = true; me.restoreSize = state.size; me.restorePos = state.pos; } else { Ext.apply(me, { width: state.size.width, height: state.size.height, x: state.pos[0], y: state.pos[1] }); } } }, // private onMouseDown: function () { if (this.floating) { this.toFront(); } }, // private onRender: function(ct, position) { var me = this; me.callParent(arguments); me.focusEl = me.el; // Double clicking a header will toggleMaximize if (me.maximizable) { me.header.on({ dblclick: { fn: me.toggleMaximize, element: 'el', scope: me } }); } }, // private afterRender: function() { var me = this, hidden = me.hidden, keyMap; me.hidden = false; // Component's afterRender sizes and positions the Component me.callParent(); me.hidden = hidden; // Create the proxy after the size has been applied in Component.afterRender me.proxy = me.getProxy(); // clickToRaise me.mon(me.el, 'mousedown', me.onMouseDown, me); // Initialize if (me.maximized) { me.maximized = false; me.maximize(); } if (me.closable) { keyMap = me.getKeyMap(); keyMap.on(27, me.onEsc, me); keyMap.disable(); } }, /** * @private * @override * Override Component.initDraggable. * Window uses the header element as the delegate. */ initDraggable: function() { var me = this, ddConfig; if (!me.header) { me.updateHeader(true); } ddConfig = Ext.applyIf({ el: me.el, delegate: '#' + me.header.id }, me.draggable); // Add extra configs if Window is specified to be constrained if (me.constrain || me.constrainHeader) { ddConfig.constrain = me.constrain; ddConfig.constrainDelegate = me.constrainHeader; ddConfig.constrainTo = me.constrainTo || me.container; } /** * <p>If this Window is configured {@link #draggable}, this property will contain * an instance of {@link Ext.util.ComponentDragger} (A subclass of {@link Ext.dd.DragTracker DragTracker}) * which handles dragging the Window's DOM Element, and constraining according to the {@link #constrain} * and {@link #constrainHeader} .</p> * <p>This has implementations of <code>onBeforeStart</code>, <code>onDrag</code> and <code>onEnd</code> * which perform the dragging action. If extra logic is needed at these points, use * {@link Ext.Function#createInterceptor createInterceptor} or {@link Ext.Function#createSequence createSequence} to * augment the existing implementations.</p> * @type Ext.util.ComponentDragger * @property dd */ me.dd = Ext.create('Ext.util.ComponentDragger', this, ddConfig); me.relayEvents(me.dd, ['dragstart', 'drag', 'dragend']); }, // private onEsc: function(k, e) { e.stopEvent(); this[this.closeAction](); }, // private beforeDestroy: function() { var me = this; if (me.rendered) { delete this.animateTarget; me.hide(); Ext.destroy( me.keyMap ); } me.callParent(); }, /** * @private * @override * Contribute class-specific tools to the header. * Called by Panel's initTools. */ addTools: function() { var me = this; // Call Panel's initTools me.callParent(); if (me.minimizable) { me.addTool({ type: 'minimize', handler: Ext.Function.bind(me.minimize, me, []) }); } if (me.maximizable) { me.addTool({ type: 'maximize', handler: Ext.Function.bind(me.maximize, me, []) }); me.addTool({ type: 'restore', handler: Ext.Function.bind(me.restore, me, []), hidden: true }); } }, /** * Gets the configured default focus item. If a {@link #defaultFocus} is set, it will receive focus, otherwise the * Container itself will receive focus. */ getFocusEl: function() { var me = this, f = me.focusEl, defaultComp = me.defaultButton || me.defaultFocus, t = typeof db, el, ct; if (Ext.isDefined(defaultComp)) { if (Ext.isNumber(defaultComp)) { f = me.query('button')[defaultComp]; } else if (Ext.isString(defaultComp)) { f = me.down('#' + defaultComp); } else { f = defaultComp; } } return f || me.focusEl; }, // private beforeShow: function() { this.callParent(); if (this.expandOnShow) { this.expand(false); } }, // private afterShow: function(animateTarget) { var me = this, size; // Perform superclass's afterShow tasks // Which might include animating a proxy from an animTarget me.callParent(arguments); if (me.maximized) { me.fitContainer(); } if (me.monitorResize || me.constrain || me.constrainHeader) { Ext.EventManager.onWindowResize(me.onWindowResize, me); } me.doConstrain(); if (me.keyMap) { me.keyMap.enable(); } }, // private doClose: function() { var me = this; // immediate close if (me.hidden) { me.fireEvent('close', me); me[me.closeAction](); } else { // close after hiding me.hide(me.animTarget, me.doClose, me); } }, // private afterHide: function() { var me = this; // No longer subscribe to resizing now that we're hidden if (me.monitorResize || me.constrain || me.constrainHeader) { Ext.EventManager.removeResizeListener(me.onWindowResize, me); } // Turn off keyboard handling once window is hidden if (me.keyMap) { me.keyMap.disable(); } // Perform superclass's afterHide tasks. me.callParent(arguments); }, // private onWindowResize: function() { if (this.maximized) { this.fitContainer(); } this.doConstrain(); }, /** * Placeholder method for minimizing the window. By default, this method simply fires the {@link #minimize} event * since the behavior of minimizing a window is application-specific. To implement custom minimize behavior, * either the minimize event can be handled or this method can be overridden. * @return {Ext.window.Window} this */ minimize: function() { this.fireEvent('minimize', this); return this; }, afterCollapse: function() { var me = this; if (me.maximizable) { me.tools.maximize.hide(); me.tools.restore.hide(); } if (me.resizer) { me.resizer.disable(); } me.callParent(arguments); }, afterExpand: function() { var me = this; if (me.maximized) { me.tools.restore.show(); } else if (me.maximizable) { me.tools.maximize.show(); } if (me.resizer) { me.resizer.enable(); } me.callParent(arguments); }, /** * Fits the window within its current container and automatically replaces * the {@link #maximizable 'maximize' tool button} with the 'restore' tool button. * Also see {@link #toggleMaximize}. * @return {Ext.window.Window} this */ maximize: function() { var me = this; if (!me.maximized) { me.expand(false); if (!me.hasSavedRestore) { me.restoreSize = me.getSize(); me.restorePos = me.getPosition(true); } if (me.maximizable) { me.tools.maximize.hide(); me.tools.restore.show(); } me.maximized = true; me.el.disableShadow(); if (me.dd) { me.dd.disable(); } if (me.collapseTool) { me.collapseTool.hide(); } me.el.addCls(Ext.baseCSSPrefix + 'window-maximized'); me.container.addCls(Ext.baseCSSPrefix + 'window-maximized-ct'); me.setPosition(0, 0); me.fitContainer(); me.fireEvent('maximize', me); } return me; }, /** * Restores a {@link #maximizable maximized} window back to its original * size and position prior to being maximized and also replaces * the 'restore' tool button with the 'maximize' tool button. * Also see {@link #toggleMaximize}. * @return {Ext.window.Window} this */ restore: function() { var me = this, tools = me.tools; if (me.maximized) { delete me.hasSavedRestore; me.removeCls(Ext.baseCSSPrefix + 'window-maximized'); // Toggle tool visibility if (tools.restore) { tools.restore.hide(); } if (tools.maximize) { tools.maximize.show(); } if (me.collapseTool) { me.collapseTool.show(); } // Restore the position/sizing me.setPosition(me.restorePos); me.setSize(me.restoreSize); // Unset old position/sizing delete me.restorePos; delete me.restoreSize; me.maximized = false; me.el.enableShadow(true); // Allow users to drag and drop again if (me.dd) { me.dd.enable(); } me.container.removeCls(Ext.baseCSSPrefix + 'window-maximized-ct'); me.doConstrain(); me.fireEvent('restore', me); } return me; }, /** * A shortcut method for toggling between {@link #maximize} and {@link #restore} based on the current maximized * state of the window. * @return {Ext.window.Window} this */ toggleMaximize: function() { return this[this.maximized ? 'restore': 'maximize'](); } /** * @cfg {Boolean} autoWidth @hide * Absolute positioned element and therefore cannot support autoWidth. * A width is a required configuration. **/ });