UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

596 lines (513 loc) 16.8 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2011 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Tino Butz (tbtz) * Christopher Zuendorf (czuendorf) ************************************************************************ */ /** * The page manager decides automatically whether the added pages should be * displayed in a master/detail view (for tablet) or as a plain card layout (for * smartphones). * * *Example* * * Here is a little example of how to use the manager. * * <pre class='javascript'> * var manager = new qx.ui.mobile.page.Manager(); * var page1 = new qx.ui.mobile.page.NavigationPage(); * var page2 = new qx.ui.mobile.page.NavigationPage(); * var page3 = new qx.ui.mobile.page.NavigationPage(); * manager.addMaster(page1); * manager.addDetail([page2,page3]); * * page1.show(); * </pre> * * */ qx.Class.define("qx.ui.mobile.page.Manager", { extend: qx.core.Object, /* ***************************************************************************** CONSTRUCTOR ***************************************************************************** */ /** * @param isTablet {Boolean?} flag which triggers the manager to layout for tablet (or big screens/displays) or mobile devices. If parameter is null, * qx.core.Environment.get("device.type") is called for decision. * @param root {qx.ui.mobile.core.Widget?} widget which should be used as root for this manager. */ construct(isTablet, root) { super(); root = root || qx.core.Init.getApplication().getRoot(); if (typeof isTablet !== "undefined" && isTablet !== null) { this.__isTablet = isTablet; } else { // If isTablet is undefined, call environment variable "device.type". // When "tablet" or "desktop" type >> do tablet layouting. this.__isTablet = qx.core.Environment.get("device.type") == "desktop" || qx.core.Environment.get("device.type") == "tablet"; } this.__detailNavigation = this._createDetailNavigation(); this.__detailNavigation.getNavigationBar().hide(); if (this.__isTablet) { this.__masterNavigation = this._createMasterNavigation(); this.__masterNavigation.getNavigationBar().hide(); this.__masterContainer = this._createMasterContainer(); this.__detailContainer = this._createDetailContainer(); this.__masterButton = this._createMasterButton(); this.__masterButton.addListener("tap", this._onMasterButtonTap, this); this.__hideMasterButton = this._createHideMasterButton(); this.__hideMasterButton.addListener( "tap", this._onHideMasterButtonTap, this ); this.__masterNavigation.addListener( "update", this._onMasterContainerUpdate, this ); this.__detailNavigation.addListener( "update", this._onDetailContainerUpdate, this ); root.add(this.__detailContainer, { flex: 1 }); this.__masterContainer.add(this.__masterNavigation, { flex: 1 }); this.__detailContainer.add(this.__detailNavigation, { flex: 1 }); qx.event.Registration.addListener( window, "orientationchange", this._onLayoutChange, this ); this.__masterContainer.addListener("resize", this._onLayoutChange, this); // On Tablet Mode, no Animation should be shown by default. this.__masterNavigation.getLayout().setShowAnimation(false); this.__detailNavigation.getLayout().setShowAnimation(false); this.__masterContainer.forceHide(); setTimeout( function () { if (qx.bom.Viewport.isLandscape()) { this.__masterContainer.show(); } }.bind(this), 300 ); } else { root.add(this.__detailNavigation, { flex: 1 }); } }, properties: { /** * The caption/label of the Master Button and Popup Title. */ masterTitle: { init: "Master", check: "String", apply: "_applyMasterTitle" }, /** * The caption/label of the Hide Master Button. */ hideMasterButtonCaption: { init: "Hide", check: "String", apply: "_applyHideMasterButtonCaption" }, /** * This flag controls whether the MasterContainer can be hidden on Landscape. */ allowMasterHideOnLandscape: { init: true, check: "Boolean" }, /** * This flag controls whether the MasterContainer hides on portrait view, * when a Detail Page fires the lifecycle event "start". */ hideMasterOnDetailStart: { init: true, check: "Boolean" } }, /* ***************************************************************************** MEMBERS ***************************************************************************** */ members: { __isTablet: null, __detailNavigation: null, __masterNavigation: null, __masterButton: null, __hideMasterButton: null, __masterPages: null, __detailPages: null, __masterContainer: null, __detailContainer: null, /** * Creates the master container. * * @return {qx.ui.mobile.container.Composite} The created container */ _createMasterContainer() { var masterContainer = new qx.ui.mobile.container.Drawer( null, new qx.ui.mobile.layout.HBox() ).set({ hideOnParentTap: false, hideOnBack: false }); masterContainer.addCssClass("master-detail-master"); masterContainer.addListener( "changeVisibility", this._onMasterChangeVisibility, this ); return masterContainer; }, /** * Creates the detail container. * * @return {qx.ui.mobile.container.Composite} The created container */ _createDetailContainer() { var detailContainer = new qx.ui.mobile.container.Composite( new qx.ui.mobile.layout.VBox() ); detailContainer.setDefaultCssClass("master-detail-detail"); return detailContainer; }, /** * Getter for the Master Container * @return {qx.ui.mobile.container.Drawer} The Master Container. */ getMasterContainer() { return this.__masterContainer; }, /** * Getter for the Detail Container * @return {qx.ui.mobile.container.Composite} The Detail Container. */ getDetailContainer() { return this.__detailContainer; }, /** * Returns the button for showing/hiding the masterContainer. * @return {qx.ui.mobile.navigationbar.Button} */ getMasterButton() { return this.__masterButton; }, /** * Returns the masterNavigation. * @return {qx.ui.mobile.container.Navigation} */ getMasterNavigation() { return this.__masterNavigation; }, /** * Returns the detailNavigation. * @return {qx.ui.mobile.container.Navigation} */ getDetailNavigation() { return this.__detailNavigation; }, /** * Factory method for the master button, which is responsible for showing/hiding masterContainer. * @return {qx.ui.mobile.navigationbar.Button} */ _createMasterButton() { return new qx.ui.mobile.navigationbar.Button(this.getMasterTitle()); }, /** * Factory method for the hide master button, which is responsible for hiding masterContainer on Landscape view. * @return {qx.ui.mobile.navigationbar.Button} */ _createHideMasterButton() { return new qx.ui.mobile.navigationbar.Button("Hide"); }, /** * Factory method for masterNavigation. * @return {qx.ui.mobile.container.Navigation} */ _createMasterNavigation() { return new qx.ui.mobile.container.Navigation(); }, /** * Factory method for detailNavigation. * @return {qx.ui.mobile.container.Navigation} */ _createDetailNavigation() { return new qx.ui.mobile.container.Navigation(); }, /** * Adds an array of NavigationPages to masterContainer, if __isTablet is true. Otherwise it will be added to detailContainer. * @param pages {qx.ui.mobile.page.NavigationPage[]|qx.ui.mobile.page.NavigationPage} Array of NavigationPages or single NavigationPage. */ addMaster(pages) { if (this.__isTablet) { if (pages) { if (!qx.lang.Type.isArray(pages)) { pages = [pages]; } for (var i = 0; i < pages.length; i++) { var masterPage = pages[i]; masterPage.addListener("start", this._onMasterPageStart, this); } if (this.__masterPages) { this.__masterPages.concat(pages); } else { this.__masterPages = pages; } this._add(pages, this.__masterNavigation); } } else { this.addDetail(pages); } }, /** * Adds an array of NavigationPage to the detailContainer. * @param pages {qx.ui.mobile.page.NavigationPage[]|qx.ui.mobile.page.NavigationPage} Array of NavigationPages or single NavigationPage. */ addDetail(pages) { this._add(pages, this.__detailNavigation); if (pages && this.__isTablet) { if (!qx.lang.Type.isArray(pages)) { pages = [pages]; } for (var i = 0; i < pages.length; i++) { var detailPage = pages[i]; detailPage.addListener("start", this._onDetailPageStart, this); } if (this.__detailPages) { this.__detailPages.concat(pages); } else { this.__detailPages = pages; } } }, /** * Called when a detailPage reaches lifecycle state "start". * @param evt {qx.event.type.Event} source event. */ _onDetailPageStart(evt) { if (qx.bom.Viewport.isPortrait() && this.isHideMasterOnDetailStart()) { this.__masterContainer.hide(); } }, /** * Called when a masterPage reaches lifecycle state "start". Then property masterTitle will be update with masterPage's title. * @param evt {qx.event.type.Event} source event. */ _onMasterPageStart(evt) { var masterPage = evt.getTarget(); var masterPageTitle = masterPage.getTitle(); this.setMasterTitle(masterPageTitle); }, /** * Adds an array of NavigationPage to the target container. * @param pages {qx.ui.mobile.page.NavigationPage[]|qx.ui.mobile.page.NavigationPage} Array of NavigationPages, or NavigationPage. * @param target {qx.ui.mobile.container.Navigation} target navigation container. */ _add(pages, target) { if (!qx.lang.Type.isArray(pages)) { pages = [pages]; } for (var i = 0; i < pages.length; i++) { var page = pages[i]; if (qx.core.Environment.get("qx.debug")) { this.assertInstance(page, qx.ui.mobile.page.NavigationPage); } if (this.__isTablet && !page.getShowBackButtonOnTablet()) { page.setShowBackButton(false); } page.setIsTablet(this.__isTablet); target.add(page); } }, /** * Called when masterContainer is updated. * @param evt {qx.event.type.Data} source event. */ _onMasterContainerUpdate(evt) { var widget = evt.getData(); widget.getRightContainer().remove(this.__hideMasterButton); widget.getRightContainer().add(this.__hideMasterButton); }, /** * Called when detailContainer is updated. * @param evt {qx.event.type.Data} source event. */ _onDetailContainerUpdate(evt) { var widget = evt.getData(); widget.getLeftContainer().remove(this.__masterButton); widget.getLeftContainer().add(this.__masterButton); }, /** * Called when user taps on masterButton. */ _onMasterButtonTap() { this.__masterContainer.show(); }, /** * Called when user taps on hideMasterButton. */ _onHideMasterButtonTap() { this._removeDetailContainerGap(); this.__masterContainer.hide(); }, /** * Event handler for <code>changeVisibility</code> event on master container. * @param evt {qx.event.type.Data} the change event. */ _onMasterChangeVisibility(evt) { var isMasterVisible = "visible" === evt.getData(); if (qx.bom.Viewport.isLandscape()) { if (this.isAllowMasterHideOnLandscape()) { if (isMasterVisible) { this._createDetailContainerGap(); this.__masterButton.exclude(); this.__hideMasterButton.show(); } else { this.__masterButton.show(); this.__hideMasterButton.show(); } } else { this.__masterButton.exclude(); this.__hideMasterButton.exclude(); } } else { this._removeDetailContainerGap(); this.__masterButton.show(); this.__hideMasterButton.show(); } }, /** * Called when layout of masterDetailContainer changes. */ _onLayoutChange() { if (this.__isTablet) { if (qx.bom.Viewport.isLandscape()) { this.__masterContainer.setHideOnParentTap(false); if (this.__masterContainer.isHidden()) { this.__masterContainer.show(); } else { this._removeDetailContainerGap(); this.__masterContainer.hide(); } } else { this._removeDetailContainerGap(); this.__masterContainer.setHideOnParentTap(true); this.__masterContainer.hide(); } } }, /** * Returns the corresponding CSS property key which fits to the drawer's orientation. * @return {String} the CSS property key. */ _getGapPropertyKey() { return ( "padding" + qx.lang.String.capitalize(this.__masterContainer.getOrientation()) ); }, /** * Moves detailContainer to the right edge of MasterContainer. * Creates spaces for aligning master and detail container aside each other. */ _createDetailContainerGap() { qx.bom.element.Style.set( this.__detailContainer.getContainerElement(), this._getGapPropertyKey(), this.__masterContainer.getSize() / 16 + "rem" ); qx.event.Registration.fireEvent(window, "resize"); }, /** * Moves detailContainer to the left edge of viewport. */ _removeDetailContainerGap() { qx.bom.element.Style.set( this.__detailContainer.getContainerElement(), this._getGapPropertyKey(), null ); qx.event.Registration.fireEvent(window, "resize"); }, /** * Called on property changes of hideMasterButtonCaption. * @param value {String} new caption * @param old {String} previous caption */ _applyHideMasterButtonCaption(value, old) { if (this.__isTablet) { this.__hideMasterButton.setLabel(value); } }, /** * Called on property changes of masterTitle. * @param value {String} new title * @param old {String} previous title */ _applyMasterTitle(value, old) { if (this.__isTablet) { this.__masterButton.setLabel(value); } } }, /* ***************************************************************************** DESTRUCTOR ***************************************************************************** */ destruct() { if (this.__masterPages) { for (var i = 0; i < this.__masterPages.length; i++) { var masterPage = this.__masterPages[i]; masterPage.removeListener("start", this._onMasterPageStart, this); } } if (this.___detailPages) { for (var j = 0; j < this.___detailPages.length; j++) { var detailPage = this.___detailPages[j]; detailPage.removeListener("start", this._onDetailPageStart, this); } } if (this.__isTablet) { this.__masterContainer.removeListener( "changeVisibility", this._onMasterChangeVisibility, this ); this.__masterContainer.removeListener( "resize", this._onLayoutChange, this ); qx.event.Registration.removeListener( window, "orientationchange", this._onLayoutChange, this ); } this.__masterPages = this.__detailPages = null; this._disposeObjects( "__detailNavigation", "__masterNavigation", "__masterButton" ); } });