/** * 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. * * 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. * * By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element specify * {@link Ext.Component#renderTo renderTo}. * * **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.** * * @example * 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(); */ 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.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.Element Element} (The Element that the Window is rendered to). */ /** * @cfg {Boolean} [modal=false] * True to make the window modal and mask everything behind it when displayed, false to display it without * restricting access to other UI elements. */ /** * @cfg {String/Ext.Element} [animateTarget=null] * Id or element from which the window should animate while opening. */ /** * @cfg {String/Number/Ext.Component} defaultFocus * Specifies a Component to receive focus when this Window is focused. * * This may be one of: * * - The index of a footer Button. * - The id or {@link Ext.AbstractComponent#itemId} of a descendant Component. * - A Component. */ /** * @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 {@link Ext#emptyFn Ext.emptyFn}. */ /** * @cfg {Boolean} [collapsed=false] * True to render the window collapsed, false to render it expanded. Note that if {@link #expandOnShow} * is true (the default) it will override the `collapsed` config and the window will always be * expanded when shown. */ /** * @cfg {Boolean} [maximized=false] * True to initially display the window in a maximized state. */ /** * @cfg {String} [baseCls='x-window'] * The base CSS class to apply to this panel's element. */ baseCls: Ext.baseCSSPrefix + 'window', /** * @cfg {Boolean/Object} resizable * Specify as `true` to allow user resizing at each edge and corner of the window, false to disable resizing. * * This may also be specified as a config object to Ext.resizer.Resizer */ resizable: true, /** * @cfg {Boolean} draggable * True to allow the window to be dragged by the header bar, false to disable dragging. 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);). */ 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}. 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. * 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. */ 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. 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. 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. */ expandOnShow: true, // inherited docs, same default collapsible: false, /** * @cfg {Boolean} closable * 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. * * 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 _{@link Ext.Component#destroy destroy}_ the * Window and its content meaning that it may not be reused. * * To make closing a Window _hide_ the Window so that it may be reused, set {@link #closeAction} to 'hide'. */ closable: true, * @cfg {Boolean} hidden * Render this Window hidden. If `true`, 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 (e) { var preventFocus; if (this.floating) { if (Ext.fly(e.getTarget()).focusable()) { preventFocus = true; } this.toFront(preventFocus); } }, // 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); // allow the element to be focusable me.el.set({ tabIndex: -1 }); // Initialize if (me.maximized) { me.maximized = false; me.maximize(); } if (me.closable) { keyMap = me.getKeyMap(); keyMap.on(27, me.onEsc, me); //if (hidden) { ? would be consistent w/before/afterShow... keyMap.disable(); //} } if (!hidden) { me.syncMonitorWindowResize(); me.doConstrain(); } }, /** * @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); } /* * Check the header here again. If for whatever reason it wasn't created in * updateHeader (preventHeader) then we'll just ignore the rest since the * header acts as the drag handle. */ if (me.header) { 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; } /** * @property {Ext.util.ComponentDragger} dd * 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} . * * This has implementations of `onBeforeStart`, `onDrag` and `onEnd` 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. */ 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, animating = animateTarget || me.animateTarget; // No constraining code needs to go here. // Component.onShow constrains the Component. *If the constrain config is true* // Perform superclass's afterShow tasks // Which might include animating a proxy from an animateTarget me.callParent(arguments); if (me.maximized) { me.fitContainer(); } me.syncMonitorWindowResize(); if (!animating) { me.doConstrain(); } if (me.keyMap) { me.keyMap.enable(); } }, // private doClose: function() { var me = this; // Being called as callback after going through the hide call below if (me.hidden) { me.fireEvent('close', me); if (me.closeAction == 'destroy') { this.destroy(); } } else { // close after hiding me.hide(me.animateTarget, me.doClose, me); } }, // private afterHide: function() { var me = this; // No longer subscribe to resizing now that we're hidden me.syncMonitorWindowResize(); // 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.syncMonitorWindowResize(); 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.syncMonitorWindowResize(); me.doConstrain(); me.fireEvent('restore', me); } return me; }, /** * Synchronizes the presence of our listener for window resize events. This method * should be called whenever this status might change. * @private */ syncMonitorWindowResize: function () { var me = this, currentlyMonitoring = me._monitoringResize, // all the states where we should be listening to window resize: yes = me.monitorResize || me.constrain || me.constrainHeader || me.maximized, // all the states where we veto this: veto = me.hidden || me.destroying || me.isDestroyed; if (yes && !veto) { // we should be listening... if (!currentlyMonitoring) { // but we aren't, so set it up Ext.EventManager.onWindowResize(me.onWindowResize, me); me._monitoringResize = true; } } else if (currentlyMonitoring) { // we should not be listening, but we are, so tear it down Ext.EventManager.removeResizeListener(me.onWindowResize, me); me._monitoringResize = false; } }, /** * 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. **/ });