Advanced TabBox

A control class contributed by Francesco Danti, demonstrating a XULTabBox extended to add several new features including automated adding and removing of tabs, auto-close, adding tabs in background and scrolling tabs horizontally.

Updated: several new features. See also the documentation and the forum thread.

Updated 2: minor bugfixes and separation of component and sample code.


The XULAdvancedTabBox component:
    <?php

    define
('_ADV_TABBOX_MENU_CLOSE_ALL','Close all tabs');
    
define('_ADV_TABBOX_MENU_CLOSE_ALL_EXCEPT_ACTIVE','Close all tabs except active tab');
    
define('_ADV_TABBOX_MENU_CLOSE_ONLY_ACTIVE','Close only active tab');

    
/**
    * XULAdvancedTabBox
    *
    * @package XULDataComponents
    * @subpackage XULContainers   
    * @author Francesco Danti
    * @author Cesare Bevacqua
    * @copyright Oracol Tech s.r.l.
    * @link http://www.oracoltech.com
    * @version 2010
    */
    
class XULAdvancedTabBox extends XULTabBox {
       public 
$tabs = array();
       public 
$panels = array();
       public 
$tabMenu;
       
       public 
$ifEmptyFlexZero FALSE;
       public 
$restoreFlex 20;
       public 
$hideComponentIfEmpty= array();
       
       
/**
        * XULAdvancedTabBox::__construct()
        *
        * @return void
        */
       
public function __construct() {      
          
parent::__construct();
          
          
$this->tabs = new XULTabs();
          
$this->panels = new XULTabPanels();
          
$this->tabMenu = new tabMenu($this);
          
$this->addChild(new XULPopupSet($this->tabMenu));
          
$this->dropMarker = new XULDropmarker();
          
$this->dropMarker->popup($this->tabMenu);
          
$this->addChild(new XULHBox(new XULAdvancedTabBoxArrowScrollBox($this$this->tabs) , new XULSpacer(1) , $this->dropMarker) , $this->panels);
          
$this->dropMarker->hidden(TRUE);
          
$this->setEventHandler('onAfterAttach'$this'setEventsAndDoFirstLoad');
       }
       
       
/**
        * XULAdvancedTabBox::setEventsAndDoFirstLoad()
        * @internal
        * @return void
        */
       
public function setEventsAndDoFirstLoad($e) {
          
$this->panels->margin(0)->padding(0);
          
$this->dropMarker->className('dropmarkerButtons');
       }
       
       
/**
        * XULAdvancedTabBox::addTabbedPanel()
        * @param string name label of the tab
        * @param node panel content panel to be attached
        * @param bool background open in background
        * @return void
        */
       
public function addTabbedPanel($name$panel$background false) {
          
          
$tab = new XULAdvancedTab($name);
          
$this->tabs->addChild($tab);
          
$this->panels->addChild(new XULAdvancedTabPanel($this$tab$panel));
          
$tab->build($this);
          
$this->tabMenu->addMenu($tab);
          
          if (!
$background$this->selectedTab($tab);
          
$this->checkIfEmptyFlexZero();
       }
       
       
/**
        * XULAdvancedTabBox::removeTabbedPanel()
        *
        * @param node panel content panel to be removed
        * @return void
        */
       
public function removeTabbedPanel($panel) {
          
          
$panel->parent->selfClose();
          return;
       }
       
       
/**
        * XULAdvancedTabBox::removeTabbedTab()
        *
        * @param node tab tab to be removed
        * @return int the posintion index of the item
        */
       
public function removeTabbedTab($tab) {
          
          
$pos $this->tabs->findChild($tab);
          
$focused = ($tab === $this->tabs->selectedItem());
          
$this->tabs->children[$pos]->removeNode();
          
$this->panels->children[$pos]->removeNode();
          
$pos = ($pos 0) ? $pos 0;
          
          if (
$focused && !$this->isEmpty()) $this->tabs->selectedItem($this->tabs->children[$pos]);
          
$this->checkIfEmptyFlexZero();
          return 
$pos;
       }
       
       
/**
        * XULAdvancedTabBox::closeAll()
        *
        * @return void
        */
       
public function closeAll($e NULL$leaveActive FALSE$posActive = - 1) {
          
          if(
$this->isEmpty()) return;
          
          if (
$leaveActive) {
             
$pos $this->tabs->findChild($this->tabs->selectedItem());
             
$selectedTab $this->tabs->children[$pos]->extractNode();
             
$selectedPan $this->panels->children[$pos]->extractNode();
          }
          
$this->tabs->removeChildren();
          
$this->panels->removeChildren();
          
          if (
$leaveActive$this->addTabbedPanel($selectedTab->label$selectedPan);
          
$this->checkIfEmptyFlexZero();
       }
       
       
/**
        * XULAdvancedTabBox::closeAllNoActive()
        *
        * @return void
        */
       
public function closeAllNoActive($e NULL) {
          
          
$this->closeAll(NULLTRUE);
       }
       
       
/**
        * XULAdvancedTabBox::closeActiveTab()
        *
        * @return void
        */
       
public function closeActiveTab($e NULL) {
          
          
$pos $this->removeTabbedTab($this->tabs->selectedItem());
          
$this->checkIfEmptyFlexZero();
          
          if (
$this->isEmpty()) return;
          
$selectNext $pos 1;
          
$this->selectedTab($this->tabs->children[($selectNext 0) ? $selectNext]);
       }
       
       
/**
        * XULAdvancedTabBox::isEmpty()
        *
        * @return bool
        */
       
public function isEmpty() {
          
          if (
count($this->tabs->children) > 0) return false;
          return 
true;
       }
       
       
/**
        * XULAdvancedTabBox::setIfEmptyFlexZero()
        *
        * @return void
        */
       
public function setIfEmptyFlexZero($bool) {
          
          
$this->ifEmptyFlexZero $bool;
          
$this->restoreFlex $this->flex;
          
$this->checkIfEmptyFlexZero();
       }
       
       
/**
        * XULAdvancedTabBox::addHideComponentIfEmpty()
        *
        * @return void
        */
       
public function addHideComponentIfEmpty($component) {
          
$this->hideComponentIfEmpty[] = $component;
       }
       
       
/**
        * XULAdvancedTabBox::checkIfEmptyFlexZero()
        *
        * @return void
        */
       
private function checkIfEmptyFlexZero() {
          
          if (
$this->ifEmptyFlexZero) {
             
             if (
$this->isEmpty()) {
                
$this->flex(0);
                
$this->hidden(TRUE);
                foreach(
$this->hideComponentIfEmpty as $cmp)
                   
$cmp->hidden(TRUE);
             } else {
                
$this->flex($this->restoreFlex);         
                
$this->hidden(FALSE);
                foreach(
$this->hideComponentIfEmpty as $cmp)
                   
$cmp->hidden(FALSE);
             }
          }
          
          
$this->dropMarker->hidden($this->isEmpty());
       }   
    }

    
/**
    * tabMenu
    *
    * @package XULDataComponents
    * @subpackage XULDataComponents   
    * @author Francesco Danti
    * @author Cesare Bevacqua
    * @copyright Oracol Tech s.r.l.
    * @link http://www.oracoltech.com
    * @version 2010
    */
    
class tabMenu extends XULMenuPopup {
       private 
$linkedXULAdvancedTabBox;
       private 
$stardardMenu TRUE;
       
       
/**
        * tabMenu::__construct()
        *
        * @return void
        */
       
function __construct($linkedXULAdvancedTabBox$stardardMenu true) {
          
          
parent::__construct();
          
$this->stardardMenu $stardardMenu;
          
$this->linkedXULAdvancedTabBox $linkedXULAdvancedTabBox;
          
$this->setEventHandler('onAfterAttach'$this'setEventsAndDoFirstLoad');
       }
       
       
/**
        * tabMenu::setEventsAndDoFirstLoad()
        *
        * @return void
        */
       
public function setEventsAndDoFirstLoad($e) {
          
          
$this->addStandardMenus();
          
          
$this->setEvent('popupshowing',MSG_SEND,$this,'onBeforeOpen',$this);   
       }
       
       
/**
        * tabMenu::addMenu()
        *
        * @return void
        */
       
function addMenu($tab) {
          
          
$menu = new XULMenuItemTabs($tab$this->linkedXULAdvancedTabBox);
          
$this->addChild($menu);
       }
       
       
/**
        * tabMenu::delMenu()
        *
        * @return void
        */
       
function delMenu() {

       }
       
       
/**
        * tabMenu::addStandardMenus()
        *
        * @return void
        */
       
function addStandardMenus() {
          if (!
$this->stardardMenu) return;
          
          
$mnuCloseAll = new XULMenuItem(_ADV_TABBOX_MENU_CLOSE_ALL);
          
$mnuCloseAllNoActive = new XULMenuItem(_ADV_TABBOX_MENU_CLOSE_ALL_EXCEPT_ACTIVE);
          
$mnuCloseActive = new XULMenuItem(_ADV_TABBOX_MENU_CLOSE_ONLY_ACTIVE);
          
          if(
count($this->children) > )
             
$this->addChildFirst($mnuCloseAll$mnuCloseAllNoActive$mnuCloseActive, new XULMenuSeparator());
          else
             
$this->addChild($mnuCloseAll$mnuCloseAllNoActive$mnuCloseActive, new XULMenuSeparator());
          
          
$mnuCloseAll->setEvent('command'MSG_SEND$this->linkedXULAdvancedTabBox'closeAll'$this->linkedXULAdvancedTabBox);
          
$mnuCloseAllNoActive->setEvent('command'MSG_SEND$this->linkedXULAdvancedTabBox'closeAllNoActive'$this->linkedXULAdvancedTabBox);
          
$mnuCloseActive->setEvent('command'MSG_SEND$this->linkedXULAdvancedTabBox'closeActiveTab'$this->linkedXULAdvancedTabBox);
       }
       
       public function 
onBeforeOpen($e) {
          
          
$this->removeChildren();
          
          
$this->addStandardMenus();
          
          foreach(
$this->linkedXULAdvancedTabBox->tabs->children as $tab)
             
$this->addMenu($tab);
       }
    }

    
/**
    * XULMenuItemTabs
    *
    * @package XULDataComponents
    * @subpackage XULDataComponents   
    * @author Francesco Danti
    * @author Cesare Bevacqua
    * @copyright Oracol Tech s.r.l.
    * @link http://www.oracoltech.com
    * @version 2010
    */
    
class XULMenuItemTabs extends XULMenuItem {
       private 
$linkedXULAdvancedTabBox;
       private 
$tab;
       
       
/**
        * XULMenuItemTabs::__construct()
        *
        * @return void
        */
       
function __construct($tab$linkedXULAdvancedTabBox) {
          
          
parent::__construct($tab->label);
          
$this->linkedXULAdvancedTabBox $linkedXULAdvancedTabBox;
          
$this->tab $tab;
          
$this->setEventHandler('onAfterAttach'$this'setEventsAndDoFirstLoad');
       }
       
       
/**
        * XULMenuItemTabs::setEventsAndDoFirstLoad()
        *
        * @return void
        */
       
public function setEventsAndDoFirstLoad($e) {
          
          
$this->setEvent('command'MSG_SEND$this'selectTab');
       }
       
       
/**
        * XULMenuItemTabs::selectTab()
        *
        * @return void
        */
       
function selectTab() {
          
          
$this->linkedXULAdvancedTabBox->selectedTab($this->tab);
       }
    }

    
/**
    * XULAdvancedTab
    *
    * @package XULDataComponents
    * @subpackage XULDataComponents   
    * @author Francesco Danti
    * @author Cesare Bevacqua
    * @copyright Oracol Tech s.r.l.
    * @link http://www.oracoltech.com
    * @version 2010
    */
    
class XULAdvancedTab extends XULTab {
       
       public 
$tabbox;
       public 
$contextMenu;
       
       
/**
        * XULAdvancedTab::__construct()
        *
        * @return void
        */
       
public function __construct($label$image NULL) {
          
parent::__construct();
          
$this->label($label);
          
$this->removeChildren();
          
          if (
$image$this->addChild(new XULImage($this->image));
          
$this->addChild(new XULLabel($this->label));
          
          
$this->mnuClose = new XULMenuItem('Close');
          
$this->addChild(new XULPopupSet($this->contextMenu = new XULMenuPopup($this->mnuClose)));
          
$this->setEventHandler('onAfterAttach'$this'setEventsAndDoFirstLoad');
       }
       
       
/**
        * XULAdvancedTab::setEventsAndDoFirstLoad()
        *
        * @return void
        */
       
public function setEventsAndDoFirstLoad($e) {
          
          
$this->mnuClose->setEvent('command'MSG_SEND$this'selfClosetab');
       }
       
       
/**
        * XULAdvancedTab::build()
        *
        * @return void
        */
       
public function build($tabbox) {
          
          
$this->tabbox $tabbox;
          
$this->context($this->contextMenu);
       }
       
       
/**
        * XULAdvancedTab::selfClosetab()
        *
        * @return void
        */
       
public function selfClosetab($e) {
          
          
$this->tabbox->removeTabbedTab($this);
       }
    }

    
/**
    * XULAdvancedTabBoxArrowScrollBox
    *
    * @package XULDataComponents
    * @subpackage XULDataComponents   
    * @author Francesco Danti
    * @author Cesare Bevacqua
    * @copyright Oracol Tech s.r.l.
    * @link http://www.oracoltech.com
    * @version 2010
    */
    
class XULAdvancedTabBoxArrowScrollBox extends XULArrowScrollBox {
       public 
$flex 1;
       public 
$containerTabBox;
       public 
$orient 'horizontal';
       public 
$closeButton;
       
       
/**
        * XULAdvancedTabBoxArrowScrollBox::__construct()
        *
        * @return void
        */
       
public function __construct($cont$child) {
          
          
$this->containerTabBox $cont;
          
$this->addChild($child);
       }
       
       
/**
        * XULAdvancedTabBoxArrowScrollBox::selectedTab()
        *
        * @return void
        */
       
public function selectedTab($tab NULL) {
          
          
$this->containerTabBox->selectedTab($tab);
       }
       
       
/**
        * XULAdvancedTabBoxArrowScrollBox::selectedPanel()
        *
        * @return void
        */
       
public function selectedPanel($tab NULL) {
          
          
$this->containerTabBox->selectedPanel($tab);
       }
    }

    
/**
    * XULAdvancedTabPanel
    *
    * @package XULDataComponents
    * @subpackage XULDataComponents   
    * @author Francesco Danti
    * @author Cesare Bevacqua
    * @copyright Oracol Tech s.r.l.
    * @link http://www.oracoltech.com
    * @version 2010
    */
    
class XULAdvancedTabPanel extends XULTabPanel {
       public 
$flex 1;
       private 
$tab;
       private 
$linkedXULAdvancedTabBox;
       
       
/**
        * XULAdvancedTabPanel::__construct()
        *
        * @return void
        */
       
function __construct($linkedXULAdvancedTabBox$tab$panel) {      
          
parent::__construct();
          
$this->linkedXULAdvancedTabBox $linkedXULAdvancedTabBox;
          
$this->tab $tab;
          
$this->addChild($panel);
       }
       
       
/**
        * XULAdvancedTabPanel::selfClose()
        *
        * @return void
        */
       
public function selfClose() {
          
          
$this->linkedXULAdvancedTabBox->removeTabbedTab($this->tab);
       }
    }



A sample application demonstrating the use of this component:
    <?php
    
//============================================================+
    // Author: Francesco "Abbadon1334" Danti
    //
    // (c) Copyright:
    //               Francesco Danti
    //               S.C. Oracol Tech s.r.l.
    //               www.oracoltech.com
    //               info@oracoltech.com
    //============================================================+

    
function authorizeLogin$args$user$pass ) {
      
// We'll just hardcode these credentials here, normally you'd want to do something
      // more sophisticated here...
      
return ($user == 'user' && $pass == 'password');
    }

    function 
getApplication$args ) {
      
// Return the name of the application class that should be started.
      
return 'testAdvTabBox';
    }

    class 
testAdvTabBox extends Application {
     
      public 
$centerscreen true;
       
      public function 
init$event ) {
       
        
// Set the window title and size
        
$this->window->title'Advanced TabBox Demo' );
        
$this->window->size1024740 );
       
        
$tBar = new XULToolBox(
          new 
XULToolBar(
             
$this->mnuLeftAdd = new XULToolBarButton('Add panel left')
            ,
$this->mnuLeftAddBack = new XULToolBarButton('Add panel left in background')
            ,
$this->mnuLeftCloseAll = new XULToolBarButton('Close all left')
            ,
$this->mnuLeftCloseAllExcActive = new XULToolBarButton('Close all except active left')
          ,new 
XULSpacer(1)
            ,
$this->mnuRightAdd = new XULToolBarButton('Add panel right')
            ,
$this->mnuRightAddBack = new XULToolBarButton('Add panel right in background')
            ,
$this->mnuRightCloseAll = new XULToolBarButton('Close all right')
            ,
$this->mnuRightCloseAllExcActive = new XULToolBarButton('Close all except active right')
          )
        );
       
        
$this->advTabBoxLeft = new XULAdvancedTabBox(1);
        
$this->advTabBoxRight = new XULAdvancedTabBox(3);
       
        
$mainSplitter = new XULSplitter();
        
$mainSplitter->addChild( new XULGrippy() );
       
        
$this->window->addChild(
          
$tBar
          
,new XULHBox(
            
1,       
            
$this->advTabBoxLeft
            
,$mainSplitter
            
,$this->advTabBoxRight
          
)
        );
       
        
$this->mnuLeftAdd->setEvent'command'MSG_SEND$this'leftAdd');
        
$this->mnuLeftAddBack->setEvent'command'MSG_SEND$this'leftAddBack');
        
$this->mnuLeftCloseAll->setEvent'command'MSG_SEND$this'leftCloseAll');
        
$this->mnuLeftCloseAllExcActive->setEvent'command'MSG_SEND$this'leftCloseAllExcActive');
       
        
$this->mnuRightAdd->setEvent'command'MSG_SEND$this'rightAdd');
        
$this->mnuRightAddBack->setEvent'command'MSG_SEND$this'rightAddBack');
        
$this->mnuRightCloseAll->setEvent'command'MSG_SEND$this'rightCloseAll');
        
$this->mnuRightCloseAllExcActive->setEvent'command'MSG_SEND$this'rightCloseAllExcActive');
      }
     
      public function 
leftAdd($e){
        
$this->advTabBoxLeft->addTabbedPaneldate("H:i:s",time()), new dummyPanel());
      }
      public function 
leftAddBack($e){   
        
$this->advTabBoxLeft->addTabbedPaneldate("H:i:s",time()),new dummyPanel(),TRUE);
      }
      public function 
leftCloseAll($e){   
        
$this->advTabBoxLeft->closeAll();
      } 
      public function 
leftCloseAllExcActive($e){
        
$this->advTabBoxLeft->closeAll(NULL,TRUE);
      }
     
      public function 
rightAdd($e){
        
$this->advTabBoxRight->addTabbedPaneldate("H:i:s",time()),new dummyPanel());
      }
      public function 
rightAddBack($e){
        
$this->advTabBoxRight->addTabbedPaneldate("H:i:s",time()),new dummyPanel(),TRUE);
      }
      public function 
rightCloseAll($e){
        
$this->advTabBoxRight->closeAll();
      } 
      public function 
rightCloseAllExcActive($e){
        
$this->advTabBoxRight->closeAll(NULL,TRUE);
      }
     
    }

    class 
dummyPanel extends XULVBox {
     
      public function 
__construct() {
       
        
$this->addChild( new XULLabel"dummy Text "));
        
$this->addChild( new XULLabel"dummy Text "));
        
$this->addChild( new XULLabel"dummy Text "));
        
$this->addChild( new XULLabel"dummy Text "));
        
$this->addChild( new XULLabel"dummy Text "));
        
$this->addChild( new XULLabel"dummy Text "));
        
$this->addChild( new XULLabel"dummy Text "));
        
$this->addChild( new XULLabel"dummy Text "));
        
$this->addChild( new XULLabel"dummy Text "));
        
$this->addChild( new XULLabel"dummy Text "));
        
$this->addChild( new XULLabel"dummy Text "));
      }
     
    }