How to use UI_​LISTFIELD dinamically in UI?

Example object and macros for this post can be downloaded here.
The codes are fully available in the downloaded materials, the post contains extracted lines only.

UI_LISTFIELD and UI_LISTITEM was introduced to ARCHICAD in version 18.
The main function of this user interface command pair is to give a better looking and more flexible alternative for the old All Parameters list, and to give developers the possibility to change the description and/or position of parameters conditionally. Neither of these were possible using the old list.

Command basics

The listfield is the grouping entity, represented as one main control on the UI, and the listitems are connected to it as subparts, represented as rows.
A listfield can exist without any listitems connected (as an “empty” field), but a listitem can not be visualized without a listfield.

ui_listfield _fieldID, _posX, _posY, _width, _height, bShowIcons, _stDescHeader, _stValueHeader

ui_listitem _itemID, _fieldID, "objectParamName", _childflag, "iconname", `Description text`

The list has display options:

  • show list icons associated with listitems (“bShowIcons”), preferred size is 32×15 pixels, format .png
  • show description and value header texts (“stDescHeader”, “stValueHeader”):
    if any of the 2 strings is not empty, a header row will appear, containing the text set in the 2 string variables of the command.
  • can be resized with settings dialog window: if the list is full width (_width = 444 pixels) and the page has no other ui items

Example object with icons and header option:
on the settings page, you can enable/disable icon display, and use a custom header text.
HowToUiListfield_header

Example object without icons or header:
HowToUiListfield_noHeader

Identification

Within one object and its macros, every listfield must have a unique ID (practically the page ID is good here unless you have multiple lists within one page), and every listitem must have a unique ID within the list it is associated with.

Listfield ID uses the page ID it is displayed on in the example.
The item ID starts from 1:

_fieldID = TABID_EXAMPLE
_itemID = 1
! create empty list control
ui_listfield _fieldID, _pageStartX, _pageStartY, _listFieldWidth, _listFieldHeight, bShowIcons, _stDescHeader, _stValueHeader

Listitem types

Listitems come in 3 forms:

  • Group Title: no object parameter is associated to the item, only the description field is used
    ui_listitem _itemID, _fieldID, "", 0, "", `Group Title text`
    
  • Separator: no object parameter, and no description, the result is a gray empty row
    ui_listitem _itemID, _fieldID, "", 0, "", ""
    
  • Object Parameter: an object parameter is associated to the item. Display format depends on parameter type.
    ui_listitem _itemID, _fieldID, "objectParamName", _childflag, "iconname", `Description text`
    

An item can be the child of another item (group title or parameter), by setting the _childflag from 0 to 1.
Unused command parameters can be left off optionally (short form).
The item ID is incremented after every item.
The display order of the items are encoded in the ID as well: items with smaller ID always come first, no matter where you write them in the script. In case you need to change the order of items conditionally, manipulate the item ID.

! group title item
ui_listitem _itemID, _fieldID, "", 0, "", "Group Title text"
_itemID = _itemID + 1

! parameter item without icon and custom definition as child
ui_listitem _itemID, _fieldID, "UIListfiel_L", 1
_itemID = _itemID + 1
	
! parameter item without icon and custom definition as child
ui_listitem _itemID, _fieldID, "UIListfiel_M", 1
 _itemID = _itemID + 1
	
! separator item
ui_listitem _itemID, _fieldID, "", 0, "", ""
_itemID = _itemID + 1
	
! parameter item without icon, with custom definition, not in group
ui_listitem _itemID, _fieldID, "UIListfiel_N", 0, "", `Custom Integer Text`
_itemID = _itemID + 1

! parameter items with icons, not in group
ui_listitem _itemID, _fieldID, "UIListfiel_I", 0, "ui_list_linetype_ex(1)", ""
_itemID = _itemID + 1

ui_listitem _itemID, _fieldID, "UIListfiel_J", 0, "ui_list_pen_ex(1)", ""
_itemID = _itemID + 1

Opened group:
HowToUiListfield_GroupOpen

Closed group:
HowToUiListfield_GroupClosed

Group open-closed state can not be set from GDL.

The last item defined in the example object has a description text that can be changed conditionally: it can either come from the original parameter list (“”), or it can use a custom text:

! item with custom description
if bUseCustomDesc then
	_stDescription = `Custom Angle Text`
else
	_stDescription = ""
endif
ui_listitem _itemID, _fieldID, "UIListfiel_K", 0, "ui_list_angle_ex(1)", _stDescription
_itemID = _itemID + 1

The structure of the command enables to fill up a list with listitems defined “all over the place”:
you can either write them into the UI script containing the listfield itself (as seen above), or you can define them in the UI script of called macros.
This is very convenient when you deal with complicated conditions to control the visibility/description/position of a single listitem (parameter), and you do not want to move/duplicate the condition code into the object/macro the listfield is actually scripted in.
HowToUiListfield_ex

In the example object, there are 2 called macros, handling 5-5 lines (group title + parameters) each.
There are 2 ways to avoid duplicated item IDs (and GDL warnings):

  • keep it continuous by pushing the last unused ID to the macro, and using the returned unused ID as starter for the next group
  • set ranges with fix starting points associated to each group. Make sure the interval is suitable to label all your items.

In the example objects both ways are represented (see “iExampleType” options).
Caller object:

! push actual _itemID to macro, return next unreserved id
call "listExample1_m" parameters all	bShowMacro1 = bShowMacro1,
					fieldID	= _fieldID,
					itemID	= _itemID,
		returned_parameters _itemID

Macro object:

_fieldID = fieldID
_itemID = itemID

if bShowMacro1 then
	ui_listitem _itemID, _fieldID, "", 0, "", `Items by Macro 1`
	_itemID = _itemID + 1
	ui_listitem _itemID, _fieldID, "UIListfiel_A", 1, ""
	_itemID = _itemID + 1
	ui_listitem _itemID, _fieldID, "UIListfiel_B", 1, ""
	_itemID = _itemID + 1
	ui_listitem _itemID, _fieldID, "UIListfiel_C", 1, ""
	_itemID = _itemID + 1
	ui_listitem _itemID, _fieldID, "UIListfiel_D", 1, ""
	_itemID = _itemID + 1
else
	_itemID = _itemID + 5
endif

end _itemID

Example for using fix IDs for each group.
The order of the called item groups can be swapped depending on the condition parameter, by manipulating the starting ID:

! set macro start itemID according to order settings
! start from fixed index		
! use range intervals large enough to cover item number
if iGroupOrder = 1 then
	_itemIDStart_m1 = 100
	_itemIDStart_m2 = 200
else
	_itemIDStart_m1 = 200
	_itemIDStart_m2 = 100
endif	
if bShowMacro1 then
	call "listExample1_m" parameters all	fieldID	= _fieldID,
						itemID	= _itemIDStart_m1
endif
if bShowMacro2 then
	call "listExample2_m" parameters all	fieldID	= _fieldID,
						itemID	= _itemIDStart_m2
endif