How to set up tabpage navigation in UI?

Example objects for this post can be downloaded here.

The latest fashion of User Interface design is to have all available parameters displayed and explained on the graphical User Interface, omitting the not-so-pretty All Parameters list completely.
Most objects usually have more parameters than can be fitted on a single page only, considering the available area you can work with is 444*266 pixels.

You definitely need more pages. But with more pages at hand, you will need navigation between pages.
Currently there are 3 methods you can use:

  • automatic page control from ARCHICAD, with hierarchical pages and extra navigation buttons
  • custom paging with UI_INFIELD and value lists
  • GLOB_UI_BUTTON_ID global combined with UI_BUTTON extended with UI_FUNCTION/UI_PREV/UI_NEXT commands to manipulate current page ID

One thing is common: the “gs_ui_current_page” integer parameter must be added to your object. Make it a hidden parameter.
Let’s take a look at the 3 methods.

Automatic tabpage control provided by ARCHICAD

See Example 1 in downloadable example lcf.

3 things need to be set up to make it available:

  • gs_ui_current_page” parameter, without value list
  • Hierarchical pages” enabled in the UI script tab in GDL editor (or STBit_UIUseHierarchicalPages in .xml)
  • extended UI_PAGE commands with page ID, parent ID, name and icon definitions

Why should you use this?

  • dinamic paging, no need to have continuous page ID numbers
  • page hierarchy is available, up to 3 levels (group the content as you seem fit)
  • extra navigation buttons to move in the hierachy
  • Prev-Next paging controls are automatic
  • does not take up space from the 444*266 pixels: it is positioned above the UI area
  • needs a lot less code than the UI_INFIELD version
  • easy to manage: add or remove a page with ease

The only negative side (for the time being): it is not available for Labels, Markers and Curtain Wall objects.
Introduced in ARCHICAD 18 (see Light objects).

Example:
The object should have 5 pages:

  • Dimensions
  • 2D Representation
  • Symbol Text
  • 3D Representation and Surfaces
  • MEP Connections

“Symbol Text” is a child of “2D Representation”, so the parent ID of “Symbol Text” should be the ID of “2D Representation”, while all other pages will have the fix root ID (-1) as parent.

Moreso, it is possible to hide a page from the hierarchy depending on the value of an object parameter or work environment setting.
In the example, if MEP is enabled: show Connections page, otherwise hide it from page popup control.

If you use an object parameter to omit a page, make sure you do not put that parameter on the page to be hidden (think of the consequences: you will not be able to bring it back unless you have access to the All Parameters list).

For a simple, no-hidden-page control example, see Graphical User Interface in Learning section.

First, you need to set up the dynamic tabpage list.
Usually it is enough to do this in the UI script, but since we have a MEP situation here, we are going to need this information in the Parameter script as well, so we set the ID list in master script, along with the MEP addon application query:

! ------------------------------------------------------------------------
! inicialise all possible tabpage IDs dynamically
! ------------------------------------------------------------------------

TABID_ROOT = -1

_idxTab = 1
TABID_DIMENSIONS	= _idxTab		: _idxTab = _idxTab + 1
TABID_2DREPRESENTATION	= _idxTab		: _idxTab = _idxTab + 1
TABID_TEXT		= _idxTab		: _idxTab = _idxTab + 1
TABID_3DREPRESENTATION	= _idxTab		: _idxTab = _idxTab + 1
TABID_MEP		= _idxTab		: _idxTab = _idxTab + 1

! ------------------------------------------------------------------------
! Check if MEP is enabled
! ------------------------------------------------------------------------
isavailable = 0
isInArchiCAD = APPLICATION_QUERY ("MEPMODELER", "IsAvailable()", isavailable)
isMEPEnabled = (isavailable OR isInArchiCAD = 0)

Next, we write the user interface script, using the MEP query info:

! set the title of the UI area popup
ui_dialog `My custom object settings`

! set the parameter "gs_ui_current_page" for actual page display
ui_current_page gs_ui_current_page

! ------------------------------------------------------------------------
! Display actual tabpages using extended ui_page command
! ------------------------------------------------------------------------

ui_page TABID_DIMENSIONS, TABID_ROOT, `Dimensions`, "uiTab_Dimension(1)"
gosub "pageDimensions"

ui_page TABID_2DREPRESENTATION, TABID_ROOT, `2D Representation`, "uiTab_2DAttributes(1)"
gosub "page2D"

ui_page TABID_TEXT, TABID_2DREPRESENTATION, `Symbol Text`, "uiTab_Text(1)"
gosub "pageText"

ui_page TABID_3DREPRESENTATION, TABID_ROOT, `3D Representation and Surfaces`, "uiTab_3DAttributes_color(1)"
gosub "page3D"

if isMEPEnabled then
	ui_page TABID_MEP, TABID_ROOT, `MEP Connections`, "uiTab_Connections_color(1)"
	gosub "pageMEP"
endif

! ------------------------------------------------------------------------
end
! ------------------------------------------------------------------------

"pageDimensions":
! page content script
return

"page2D":
! page content script
return

"pageText":
! page content script
return

"page3D":
! page content script
return

"pageMEP":
! page content script
return

There is an extra option to highlight the edited connection in preview if the current page is MEP Connections. This section should go in the Parameter script:

! set MEP connection indicator ON for preview if active page is Connections

ac_mep_connectionpage_active = 0
if isMEPEnabled then
	if gs_ui_current_page = TABID_MEP then
		ac_mep_connectionpage_active = 1
	endif
endif
parameters ac_mep_connectionpage_active = ac_mep_connectionpage_active

Of course, you can temporarily omit a page a lot more easily using simple object parameters. Just change the _isMEPEnabled condition to one you need.
Icons work best in the extended UI_PAGE command if their size is 18×18 pixels. The format should be .png.
The example script looks like this:
HowToCreatePaging1

Scripted tabpage control with UI_INFIELD

See Example 2 in downloadable example lcf.

This method is a bit more complicated, since you need to set up a valid value list for “gs_ui_current_page” parameter, and the Prev-Next buttons need to be scripted separately.
Moreso, Prev-Next only works well with continuous page numbering, which can make conditionally omitted pages a bit more difficult to handle.

Things you need to set up:

  • value list for “gs_ui_current_page” in parameter script, depending on the actual number of shown pages
  • continuous page numbering for Prev-Next function
  • scripted UI_INFIELD control for page-popup
  • scripted UI_BUTTON for Previous Page navigation
  • scripted UI_BUTTON for Next Page navigation
  • short version of UI_PAGE, containing only a page number as input parameter

Note: hierarchy is not available this case, so make sure you did not set the “Hierarchical pages” ON accidentally. If you do and use the short version of UI_PAGE, you get an empty automatic popup.

The only true inconvenience is the continuous page numbering issue.
Other than that, you can customize the appearance, position and size of your page control popup as you like, since it is only a regular ui_infield{3} or ui_infield{4} with method 2, for setting the “gs_ui_current_page” parameter’s value directly.
We have a standard size used in most of our Label objects, but feel free to come up with some design of your own.

Make sure you put the control onto every ui page.

Example:
same situation as the previous one, without the MEP page and the Text hierarchy.
Either you address the command’s input parameters directly, or you create corresponding arrays to fill up the paging control.

In Master script:

! set up continuous page IDs
_idxTab = 1
TABID_DIMENSIONS	= _idxTab		: _idxTab = _idxTab + 1
TABID_2DREPRESENTATION	= _idxTab		: _idxTab = _idxTab + 1
TABID_3DREPRESENTATION	= _idxTab		: _idxTab = _idxTab + 1

In Parameter script:

! set values for paging parameter
values "gs_ui_current_page" TABID_DIMENSIONS, TABID_2DREPRESENTATION, TABID_3DREPRESENTATION

In User Interface script:

! create page information arrays
dim tabTitles[3]
	tabTitles[1] = `Dimensions`
	tabTitles[2] = `2D Representation`
	tabTitles[3] = `3D Representation and Surfaces`

dim tabIcons[3]
	tabIcons[1] = "ui_tab_furniture_dim(1)"
	tabIcons[2] = "ui_tab_2d_Attributes"
	tabIcons[3] = "ui_tab_3d_attributes"

dim tabIDs[3]
	tabIDs[1] = TABID_DIMENSIONS
	tabIDs[2] = TABID_2DREPRESENTATION
	tabIDs[3] = TABID_3DREPRESENTATION

! file dependence of array for archive
file_dependence "ui_tab_furniture_dim(1)"
file_dependence "ui_tab_2d_Attributes"
file_dependence "ui_tab_3d_attributes"

! place the following control on every displayed page
ui_current_page gs_ui_current_page
ui_page gs_ui_current_page

! control popup for paging
ui_infield{4} "gs_ui_current_page", 0, 0, 350, 25,
	2, "",
	0, 0,
	30, 20, 30, 20,
	tabIcons, tabTitles, tabIDs

! go to next-previous page buttons
ui_button ui_prev, " << ", 350+10, 2, 32,21, gs_ui_current_page - 1
ui_button ui_next, " >> ", 350+10+37, 2, 32,21, gs_ui_current_page + 1

ui_page TABID_DIMENSIONS
gosub "pageDimensions"

ui_page TABID_2DREPRESENTATION
gosub "page2D"

ui_page TABID_3DREPRESENTATION
gosub "page3D"
! ------------------------------------------------------------------------
end
! ------------------------------------------------------------------------

"pageDimensions":
	ui_outfield `Dimensions Page Content`, 5, 40, 150, 15
return

"page2D":
	ui_outfield `2D Page Content`, 5, 40, 150, 15
return

"page3D":
	ui_outfield `3D Page Content`, 5, 40, 150, 15
return

If you want to omit pages conditionally, move codes to the Master script, ad use dynamic array indexing to keep the continuity of indexes when omitting a page:

! create page information arrays
dim tabTitles[]
dim tabIcons[]
dim tabIDs[]

_idx = 1

tabTitles[_idx] = `Dimensions`
tabIcons[_idx] = "ui_tab_furniture_dim(1)"
TABID_DIMENSIONS = _idx
tabIDs[_idx] = TABID_DIMENSIONS
_idx = _idx + 1

if bShow2DPage then
	tabTitles[_idx] = `2D Representation`
	tabIcons[_idx] = "ui_tab_2d_Attributes"
	TABID_2DREPRESENTATION = _idx
	tabIDs[_idx] = TABID_2DREPRESENTATION
	_idx = _idx + 1
endif

tabTitles[_idx] = `3D Representation and Surfaces`
tabIcons[_idx] = "ui_tab_3d_attributes"
TABID_3DREPRESENTATION = _idx
tabIDs[_idx] = TABID_3DREPRESENTATION
_idx = _idx + 1

In Parameter script:

values "gs_ui_current_page" tabIDs

Modify UI script part (apart from removing the arrays):

if bShow2DPage then
	ui_page TABID_2DREPRESENTATION
	gosub "page2D"
endif

Note: “bShow2DPage” is just a dummy boolean parameter for the sake of the example.
Result:
HowToCreatePaging2

UI_BUTTON paging methods

In the above example, the UI_BUTTON UI_PREV/UI_NEXT combination was used to put a traditional Prev-Next paging button on each. It is working without the UI_INFIELD command, but it needs the values set for “gs_ui_current_page” in Parameter script, still:
HowToCreatePaging3

Another, more interesting way to move between pages is to use UI_BUTTON UI_FUNCTION combination. This way it is not necessary to move to the “next” or “previous” page, but you can jump to a custom location in the page hierarchy. We call these buttons “jumpbuttons”.
The trick: use a negative page ID for GLOB_UI_BUTTON_ID, then use it in parameter script to modify the “gs_ui_current_page” parameter.
Let’s put a jumpbutton to the Dimensions page, linking to 3D Representation and Surfaces page (modify “pageDimensions” sub):

"pageDimensions":
	! jumpbutton to 3D Representation page
	! button ID is the tabID of 3D Representation
	ui_pict_button ui_function, "", "uiFunction_3D_ex", 444-36, 2, 36, 22, -TABID_3DREPRESENTATION  ! Set GLOB_UI_BUTTON_ID
return

In the parameter script, do this:

! if ID is negative, set active page to button ID
if GLOB_UI_BUTTON_ID < 0 then
	parameters gs_ui_current_page = -GLOB_UI_BUTTON_ID
endif

It should look like this:
HowToCreatePaging4

Hiding the All Parameters list

Once you have every parameter displayed on the graphical UI, you can omit the All Parameters list from the user.
HowToCreatePagingHideall
Write the following into the parameter script:

! hide all parameters list
hideparameter all "A", "B", "Zzyzx"

The parameters written after the all keyword are exceptions, so they will still be visible on the Preview and Positioning section of the settings dialog. If you remove any from the exception list, they will be missing from the Preview and Positioning dialog as well.

Command and function pool:
UI_PAGE
UI_DIALOG
UI_PICT_BUTTON
UI_INFIELD
MEP Modeler APPLICATION QUERY
GLOB_UI_BUTTON_ID
HIDEPARAMETER