UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

698 lines (570 loc) 18 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2008 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: * Sebastian Werner (wpbasti) * Andreas Ecker (ecker) * Martin Wittemann (martinwittemann) * Jonathan Weiß (jonathan_rass) * Christian Hagendorn (chris_schmidt) ************************************************************************ */ /** * A tab view is a multi page view where only one page is visible * at each moment. It is possible to switch the pages using the * buttons rendered by each page. * * Note that prior to v6.0, when changing the currently selected tab via code * (ie changing the selection property) TabView would automatically set the * focus to that tab; this is undesirable (and inconsistent with other parts * of the framework) and is no longer done automatically. * * @childControl bar {qx.ui.container.SlideBar} slidebar for all tab buttons * @childControl pane {qx.ui.container.Stack} stack container to show one tab page */ qx.Class.define("qx.ui.tabview.TabView", { extend : qx.ui.core.Widget, implement : qx.ui.core.ISingleSelection, include : [qx.ui.core.MContentPadding], /* ***************************************************************************** CONSTRUCTOR ***************************************************************************** */ /** * @param barPosition {String} Initial bar position ({@link #barPosition}) */ construct : function(barPosition) { this.base(arguments); this.__barPositionToState = { top : "barTop", right : "barRight", bottom : "barBottom", left : "barLeft" }; this._createChildControl("bar"); this._createChildControl("pane"); // Create manager var mgr = this.__radioGroup = this._createRadioGroupInstance(); mgr.setWrap(false); mgr.addListener("changeSelection", this._onChangeSelection, this); // Initialize bar position if (barPosition != null) { this.setBarPosition(barPosition); } else { this.initBarPosition(); } }, /* ***************************************************************************** EVENTS ***************************************************************************** */ events : { /** Fires after the selection was modified */ "changeSelection" : "qx.event.type.Data", /** Fires after the value was modified */ "changeValue" : "qx.event.type.Data" }, /* ***************************************************************************** PROPERTIES ***************************************************************************** */ properties : { // overridden appearance : { refine : true, init : "tabview" }, /** * This property defines on which side of the TabView the bar should be positioned. */ barPosition : { check : ["left", "right", "top", "bottom"], init : "top", apply : "_applyBarPosition" } }, /* ***************************************************************************** MEMBERS ***************************************************************************** */ members : { /** @type {qx.ui.form.RadioGroup} instance containing the radio group */ __radioGroup : null, /** * setValue implements part of the {@link qx.ui.form.IField} interface. * * @param item {null|qx.ui.tabview.Page} Page to set as selected value. * @returns {null|TypeError} The status of this operation. */ setValue : function(item) { if (null === item) { this.resetSelection(); return null; } if (item instanceof qx.ui.tabview.Page) { this.setSelection([item]); return null; } else { return new TypeError("Given argument is not null or a {qx.ui.tabview.Page}."); } }, /** * getValue implements part of the {@link qx.ui.form.IField} interface. * * @returns {null|qx.ui.tabview.Page} The currently selected page or null if there is none. */ getValue : function() { var pages = this.getSelection(); if (pages.length) { return pages[0]; } else { return null; } }, /** * resetValue implements part of the {@link qx.ui.form.IField} interface. */ resetValue : function() { this.resetSelection(); }, /* --------------------------------------------------------------------------- WIDGET API --------------------------------------------------------------------------- */ // overridden _createChildControlImpl : function(id, hash) { var control; switch(id) { case "bar": control = new qx.ui.container.SlideBar(); control.setZIndex(10); this._add(control); break; case "pane": control = new qx.ui.container.Stack; control.setZIndex(5); this._add(control, {flex:1}); break; } return control || this.base(arguments, id); }, /** * Creates the radio group manager instance. * * Allows override customizations of the instance * * @return {qx.ui.form.RadioGroup} */ _createRadioGroupInstance : function() { return new qx.ui.form.RadioGroup; }, /** * Returns the element, to which the content padding should be applied. * * @return {qx.ui.core.Widget} The content padding target. */ _getContentPaddingTarget : function() { return this.getChildControl("pane"); }, /* --------------------------------------------------------------------------- CHILDREN HANDLING --------------------------------------------------------------------------- */ /** * Adds a page to the tabview including its needed button * (contained in the page). * * @param page {qx.ui.tabview.Page} The page which should be added. */ add : function(page) { if (qx.core.Environment.get("qx.debug")) { if (!(page instanceof qx.ui.tabview.Page)) { throw new Error("Incompatible child for TabView: " + page); } } var button = page.getButton(); var bar = this.getChildControl("bar"); var pane = this.getChildControl("pane"); // Exclude page page.exclude(); // Add button and page bar.add(button); pane.add(page); // Register button this.__radioGroup.add(button); // Add state to page page.addState(this.__barPositionToState[this.getBarPosition()]); // Update states page.addState("lastTab"); var children = this.getChildren(); if (children[0] == page) { page.addState("firstTab"); } else { children[children.length-2].removeState("lastTab"); } page.addListener("close", this._onPageClose, this); }, /** * Adds a page to the tabview including its needed button * (contained in the page). * * @param page {qx.ui.tabview.Page} The page which should be added. * @param index {Integer?null} Optional position where to add the page. */ addAt : function(page, index) { if (qx.core.Environment.get("qx.debug")) { if (!(page instanceof qx.ui.tabview.Page)) { throw new Error("Incompatible child for TabView: " + page); } } var children = this.getChildren(); if(!(index == null) && index > children.length) { throw new Error("Index should be less than : " + children.length); } if(index == null) { index = children.length; } var button = page.getButton(); var bar = this.getChildControl("bar"); var pane = this.getChildControl("pane"); // Exclude page page.exclude(); // Add button and page bar.addAt(button, index); pane.addAt(page, index); // Register button this.__radioGroup.add(button); // Add state to page page.addState(this.__barPositionToState[this.getBarPosition()]); // Update states children = this.getChildren(); if(index == children.length-1) { page.addState("lastTab"); } if (children[0] == page) { page.addState("firstTab"); } else { children[children.length-2].removeState("lastTab"); } page.addListener("close", this._onPageClose, this); }, /** * Removes a page (and its corresponding button) from the TabView. * * @param page {qx.ui.tabview.Page} The page to be removed. */ remove : function(page) { var pane = this.getChildControl("pane"); var bar = this.getChildControl("bar"); var button = page.getButton(); var children = pane.getChildren(); // Try to select next page if (this.getSelection()[0] == page) { var index = children.indexOf(page); if (index == 0) { if (children[1]) { this.setSelection([children[1]]); } else { this.resetSelection(); } } else { this.setSelection([children[index-1]]); } } // Remove the button and page bar.remove(button); pane.remove(page); // Remove the button from the radio group this.__radioGroup.remove(button); // Remove state from page page.removeState(this.__barPositionToState[this.getBarPosition()]); // Update states if (page.hasState("firstTab")) { page.removeState("firstTab"); if (children[0]) { children[0].addState("firstTab"); } } if (page.hasState("lastTab")) { page.removeState("lastTab"); if (children.length > 0) { children[children.length-1].addState("lastTab"); } } page.removeListener("close", this._onPageClose, this); }, /** * Returns TabView's children widgets. * * @return {qx.ui.tabview.Page[]} List of children. */ getChildren : function() { return this.getChildControl("pane").getChildren(); }, /** * Returns the position of the given page in the TabView. * * @param page {qx.ui.tabview.Page} The page to query for. * @return {Integer} Position of the page in the TabView. */ indexOf : function(page) { return this.getChildControl("pane").indexOf(page); }, /** * Returns the radio group manager. * * @return {qx.ui.form.RadioGroup} the radio group. */ getRadioGroup : function() { return this.__radioGroup; }, /* --------------------------------------------------------------------------- APPLY ROUTINES --------------------------------------------------------------------------- */ /** @type {Map} Maps the bar position to an appearance state */ __barPositionToState : null, /** * Apply method for the placeBarOnTop-Property. * * Passes the desired value to the layout of the tabview so * that the layout can handle it. * It also sets the states to all buttons so they know the * position of the bar. * * @param value {Boolean} The new value. * @param old {Boolean} The old value. */ _applyBarPosition : function(value, old) { var bar = this.getChildControl("bar"); var pane = this.getChildControl("pane"); var horizontal = value == "left" || value == "right"; var reversed = value == "right" || value == "bottom"; var layoutClass = horizontal ? qx.ui.layout.HBox : qx.ui.layout.VBox; var layout = this._getLayout(); if (layout && layout instanceof layoutClass) { // pass } else { this._setLayout(layout = new layoutClass); } // Update reversed layout.setReversed(reversed); // Sync orientation to bar bar.setOrientation(horizontal ? "vertical" : "horizontal"); // Read children var children = this.getChildren(); var i, l; // Toggle state to bar if (old) { var oldState = this.__barPositionToState[old]; // Update bar bar.removeState(oldState); // Update pane pane.removeState(oldState); // Update pages for (i=0, l=children.length; i<l; i++) { children[i].removeState(oldState); } } if (value) { var newState = this.__barPositionToState[value]; // Update bar bar.addState(newState); // Update pane pane.addState(newState); // Update pages for (i=0, l=children.length; i<l; i++) { children[i].addState(newState); } } }, /* --------------------------------------------------------------------------- SELECTION API --------------------------------------------------------------------------- */ /** * Returns an array of currently selected items. * * Note: The result is only a set of selected items, so the order can * differ from the sequence in which the items were added. * * @return {qx.ui.tabview.Page[]} List of items. */ getSelection : function() { var buttons = this.__radioGroup.getSelection(); var result = []; for (var i = 0; i < buttons.length; i++) { result.push(buttons[i].getUserData("page")); } return result; }, /** * Replaces current selection with the given items. * * @param items {qx.ui.tabview.Page[]} Items to select. * @throws {Error} if one of the items is not a child element and if * items contains more than one elements. */ setSelection : function(items) { var buttons = []; for (var i = 0; i < items.length; i++) { buttons.push(items[i].getChildControl("button")); } this.__radioGroup.setSelection(buttons); }, /** * Clears the whole selection at once. */ resetSelection : function() { this.__radioGroup.resetSelection(); }, /** * Detects whether the given item is currently selected. * * @param item {qx.ui.tabview.Page} Any valid selectable item. * @return {Boolean} Whether the item is selected. * @throws {Error} if one of the items is not a child element. */ isSelected : function(item) { var button = item.getChildControl("button"); return this.__radioGroup.isSelected(button); }, /** * Whether the selection is empty. * * @return {Boolean} Whether the selection is empty. */ isSelectionEmpty : function() { return this.__radioGroup.isSelectionEmpty(); }, /** * Returns all elements which are selectable. * * @return {qx.ui.tabview.Page[]} The contained items. * @param all {Boolean} true for all selectables, false for the * selectables the user can interactively select */ getSelectables: function(all) { var buttons = this.__radioGroup.getSelectables(all); var result = []; for (var i = 0; i <buttons.length; i++) { result.push(buttons[i].getUserData("page")); } return result; }, /** * Event handler for <code>changeSelection</code>. * * @param e {qx.event.type.Data} Data event. */ _onChangeSelection : function(e) { var pane = this.getChildControl("pane"); var button = e.getData()[0]; var oldButton = e.getOldData()[0]; var value = []; var old = []; if (button) { value = [button.getUserData("page")]; pane.setSelection(value); this.scrollChildIntoView(button, null, null, false); } else { pane.resetSelection(); } if (oldButton) { old = [oldButton.getUserData("page")]; } this.fireDataEvent("changeSelection", value, old); }, /** * Event handler for <code>beforeChangeSelection</code>. * * @param e {qx.event.type.Event} Data event. */ _onBeforeChangeSelection : function(e) { if (!this.fireNonBubblingEvent("beforeChangeSelection", qx.event.type.Event, [false, true])) { e.preventDefault(); } }, /* --------------------------------------------------------------------------- EVENT LISTENERS --------------------------------------------------------------------------- */ /** * Event handler for the change of the selected item of the radio group. * @param e {qx.event.type.Data} The data event */ _onRadioChangeSelection : function(e) { var element = e.getData()[0]; if (element) { this.setSelection([element.getUserData("page")]); } else { this.resetSelection(); } }, /** * Removes the Page widget on which the close button was tapped. * * @param e {qx.event.type.Pointer} pointer event */ _onPageClose : function(e) { // reset the old close button states, before remove page // see http://bugzilla.qooxdoo.org/show_bug.cgi?id=3763 for details var page = e.getTarget(); var closeButton = page.getButton().getChildControl("close-button"); closeButton.reset(); this.remove(page); } }, /* ***************************************************************************** DESTRUCTOR ***************************************************************************** */ destruct : function() { this._disposeObjects("__radioGroup"); this.__barPositionToState = null; } });