@qooxdoo/framework
Version:
The JS Framework for Coders
570 lines (461 loc) • 16.7 kB
JavaScript
/* ************************************************************************
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 : function(isTablet, root)
{
this.base(arguments);
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 : function() {
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 : function() {
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 : function() {
return this.__masterContainer;
},
/**
* Getter for the Detail Container
* @return {qx.ui.mobile.container.Composite} The Detail Container.
*/
getDetailContainer : function() {
return this.__detailContainer;
},
/**
* Returns the button for showing/hiding the masterContainer.
* @return {qx.ui.mobile.navigationbar.Button}
*/
getMasterButton : function() {
return this.__masterButton;
},
/**
* Returns the masterNavigation.
* @return {qx.ui.mobile.container.Navigation}
*/
getMasterNavigation : function() {
return this.__masterNavigation;
},
/**
* Returns the detailNavigation.
* @return {qx.ui.mobile.container.Navigation}
*/
getDetailNavigation : function() {
return this.__detailNavigation;
},
/**
* Factory method for the master button, which is responsible for showing/hiding masterContainer.
* @return {qx.ui.mobile.navigationbar.Button}
*/
_createMasterButton : function() {
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 : function() {
return new qx.ui.mobile.navigationbar.Button("Hide");
},
/**
* Factory method for masterNavigation.
* @return {qx.ui.mobile.container.Navigation}
*/
_createMasterNavigation : function() {
return new qx.ui.mobile.container.Navigation();
},
/**
* Factory method for detailNavigation.
* @return {qx.ui.mobile.container.Navigation}
*/
_createDetailNavigation : function() {
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 : function(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 : function(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 : function(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 : function(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 : function(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 : function(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 : function(evt) {
var widget = evt.getData();
widget.getLeftContainer().remove(this.__masterButton);
widget.getLeftContainer().add(this.__masterButton);
},
/**
* Called when user taps on masterButton.
*/
_onMasterButtonTap : function() {
this.__masterContainer.show();
},
/**
* Called when user taps on hideMasterButton.
*/
_onHideMasterButtonTap : function() {
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: function(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 : function() {
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 : function() {
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 : function() {
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 : function() {
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 : function(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 : function(value, old) {
if(this.__isTablet) {
this.__masterButton.setLabel(value);
}
}
},
/*
*****************************************************************************
DESTRUCTOR
*****************************************************************************
*/
destruct : function()
{
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");
}
});