User:CBM-c-/VS-List Browsers part 1: Difference between revisions

From Vectorworks Developer
Jump to navigation Jump to search
(split)
(split)
Line 3: Line 3:
</div>
</div>


For better understanding please use the comprehensive example at [[User:CBM-c-/Dialog with List Browser| Dialog with List Browser]].
 
By --[[User:CBM-c-|_c_]] ([[User talk:CBM-c-|talk]]) 03:08, 31 December 2020 (EST) (previously Orso B. Schmid)
Some of you know me as Orso from the now defunct Vectorlab, I maintained it for many years. Here is the List Browsers article from the former Vectorlab site. For better understanding please use the comprehensive example at [[User:CBM-c-/Dialog with List Browser| Dialog with List Browser]].
By --[[User:CBM-c-|_c_]] ([[User talk:CBM-c-|talk]]) 01:31, 31 December 2020 (EST), previously Orso.B.Schmid, 2009-2018.
 
 
= List Browsers =
 
List Browsers allow for great layout, advanced data management, but also amazing layout features within the single cells. Is not difficult to load them with some text data and make them display a whatever list. Still the advanced settings of List Browsers are quite enigmatic. After years of trial and error, I attempt here to resume what I discovered. If you find an error, I'd be glad to be informed. I am a user and had no reference literature for this article which is based on pure experience, if you are a programmer you will probably find the terms employed not quite the right ones.
 
Since List Browsers are so complex, the best way to learn how to use them is to have some free code available for experiments. Free code for List Browsers can be counted on one hand and none of the snippets available tells you a single thing about Control Types, Edit Display Types, Item Display Types, Column Data Items and so on. Use the example [[User:CBM-c-/Dialog with List Browser| Dialog with List Browser]]. It should help experiencing the different List Browser display according to the available options.
 
For those wishing to deepen the topic: see NSTableView Class (for the MacOs environment).
 
You'll find a large amount of sub-routines for List Browsers in the page [[User:CBM-c-/VS-Subroutines - List Browsers| Subroutines - List Browsers]] (to do).




Line 15: Line 27:
|}
|}


== Introduction ==


= Columns =
The display of List Browsers is determined by the right combination of four settings:
* Control Type
* Display Type
* Cell's Column Owner
* Column Data Items
This is all -pardon my French- bloody difficult, mostly because it is not documented. It resembles a 4D-puzzle.


After creating an (optional) [[#Icon (List)| Icon List]] you can proceed to add columns to the List Browser. While creating each column, it is simple to set immediately its appearance and eventually some modifiers which affect -it goes without saying- the whole column and all cells there included. A column creates ready to display text which doesn't react on clicks. Any other display involves a resetting of [[#Control Type| Control Type]] and [[#Display Type| Display Type]].
Most of the times you create columns only at dialog setup and in a loop. If you do it only once -because you won't delete columns later- the loop needs a place in [[VS:Creating_a_Custom_Dialog_Box| SetupDialogC]] and nowhere else. Nothing forbids you, though, to delete columns and redo them according to your script needs. In this case you'll have the column creation in a sub-routine callable on demand with a wider script scope. But is seldom.


{| class="wikitable" style="margin: auto;"
{| class="wikitable" style="margin: auto;"
! Settings !! Modifiers !! Column set up
|-
|-
|
! colspan="3"| [[User:CBM-c-/VS-List_Browsers_part_2#Column_Data_Items| Column Data Items]]
* [[#Column Titles| Titles]]
! List Browser set up
* [[#Column Lines| Column Lines]]
* [[#Sorting| Column Sorting]]
 
|
* [[#Control Type| Control Type]]
* [[#Display Type| Item or Edit Display Type]]
* [[#Column Data Items| List of Column Data Items]]
 
|
Insert columns using '''InsertLBColumn'''. Set for each column:
* [[#Column Titles| title appearance]]
* if modifiers are needed, also set up:
** [[#Control Type| Control Type]] (optional)
** [[#Display Type| Item or Edit Display Type]] (optional)
** [[#Column Data Items| List of Column Data Items]] (optional)
 
Outside any loop set for the whole List Browser:
* [[#Column Lines| Column Lines]] (optional)
* [[#Drag and Drop| Drag and Drop Column]] (optional)
* [[#Sorting| Sort Column]] column (optional)
|}
 
<code lang="pas">
GetNumLBColumns( dialogID:LONGINT; componentID:LONGINT):INTEGER;
</code>
[[VS:GetNumLBColumns]] Count of columns present in a List Browser. You will use this call while adding or relating to columns. Remember to decrease the count of columns of -1, if you use it as column index reference, since -again- column indexes are 0-based
 
<code lang="pas">
InsertLBColumn( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
headerString:STRING; width:INTEGER):INTEGER;
</code>
[[VS:InsertLBColumn]] Inserts a column ready to be filled with text cells. Call '''InsertLBColumn''' for each column to be inserted, use the returned index for further column settings. Defaults upon creation are:
 
* [[#Control Type| Control Type]]: Static = 1
* [[#Display Type| Display Type]]: Text Only = 2 (Upon creation the Display type is "0", however the reason)
 
; result (0-based): index of the new column in the List Browser.
; columnIndex: at which position in the List Browser the column should be inserted.
: ''BUG: passing "0" as insertion position the title will center after the first row. Moreover in VW 13 if the List Browser has icons loaded, an icon will unexpectedly appear in the title. For this reason is always wise to insert new columns only at the end using GetNumLBColumns.''
; headerString: the column's title (string). The title aligns left by default. If a different alignment is needed it can be fixed later with [[VS:SetLBColumnHeaderJust]].
; width: the width of the column. Can be modified anytime using '''SetLBColumnWidth'''. Don't pass a zero width.
 
<code lang="pas">
GetLBColumnWidth( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
VAR width:INTEGER):BOOLEAN;
SetLBColumnWidth( dialogID:LONGINT; componentID:LONGINT;
fromColumn:INTEGER; toColumn:INTEGER; width:INTEGER):BOOLEAN;
</code>
[[VS:GetLBColumnWidth]], [[VS:SetLBColumnWidth]] Manage column widths.
 
; fromColumn, toColumn: the range of columns where the width should apply. Mind that the title seems to create a large white space at the end. You should dimension columns rather large otherwise they won't display the whole title text. Under VW 14+ you can use ''GetLBHeaderTextWidth''' to dimension precisely your column width.
Often you'll create columns in a loop from string arrays or external files data. In this case you insert the columns with some default width and reset precisely only those columns that need to be different.
: ''BUG: Column widths "0" crash VW 13 (fixed by build 87094). You must programmatically avoid them and also make sure that your user is not in the position of resizing them to zero, if you wish to grant compatibility with VW 13.''
 
<code lang="pas">
GetLBHeaderTextWidth(className: STRING; allowForSortIcon: BOOLEAN): INTEGER ;
</code>
[[VS:GetLBHeaderTextWidth]] Returns the column width needed to fit the column title without resizing.
 
 
=== Column Titles ===
 
An array of string is often a good way to create the titles in a loop. Other times data will be loaded from external files or from existing document objects. Whatever system you choose, you shall remember that column titles cannot be changed after creation. In order to change a column's title the only solution is to destroy the column and recreate it. This can be done with [[VS:DeleteLBColumn]].
 
<code lang="pas">
SetLBColumnImage(nDialogID, nComponentID :LONGINT;
nColumnIndex, nImageIndex :INTEGER) :BOOLEAN;
</code>
[[VS:SetLBColumnImage]] Sets an image as column title.
 
; result: FALSE if the column index "nColumnIndex" points to a column that doesn't exists. Note that the routine doesn't return false if the icon index "nImageIndex" points to a missing icon.
; nImageIndex: Use an icon as column title. This replaces the text, you cannot have both text and icon. The image must available in the List Browser's [[#Icon (List)| Icon List]].
 
<code lang="pas">
GetLBColumnHeaderJust( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
VAR justification:INTEGER):BOOLEAN;
SetLBColumnHeaderJust( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
justification:INTEGER):BOOLEAN;
</code>
[[VS:GetLBColumnHeaderJust]], [[VS:SetLBColumnHeaderJust]] Modify the alignment of the column title. Use following constants:
: 1 = Left
: 2 = Center
: 3 = Right
 
<code lang="pas">
GetLBColumnHeaderToolTip( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
VAR toolTipPrimaryText:STRING; VAR toolTipSubText:STRING):BOOLEAN;


SetLBColumnHeaderToolTip( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
toolTipPrimaryText:STRING; toolTipSubText:STRING):BOOLEAN;
justification:INTEGER):BOOLEAN;
</code>
[[VS:GetLBColumnHeaderToolTip]], [[VS:SetLBColumnHeaderToolTip]] Gets/sets hovering tips for the column headers. Setting tool tips is quite handy when the column width will certainly be too small and titles will be cropped: pass then titles as tool tip.
; toolTipPrimaryText: text appearing on hovering over the column title.
; toolTipSubText: text appearing pressing the cmd/alt (Mac/Win) key while hovering.
=== Column Lines ===
Column lines are the gray vertical lines drawn between each column. They are optional. Columns with control type [[#Radio| Radio]] have an extra option for setting the sub-titles column lines.
<code lang="pas">
AreLBColumnLinesEnabled(dialogID: LONGINT; componentID: LONGINT): BOOLEAN;
EnableLBColumnLines(dialogID: LONGINT; componentID: LONGINT;
enableColumnLines:BOOLEAN);
</code>
[[VS:AreLBColumnLinesEnabled]], [[VS:EnableLBColumnLines]] Checks/sets on/off status of column lines for a List Browser. Default: FALSE. Toggles the vertical column lines on/off. It affects a whole List Browser, so this call shall be kept outside repetitive routines. Usually it is called at the end of the List Browser setup routine. Don't confuse it with [[VS:EnableLBRadioColumnLines]].
<code lang="pas">
AreLBRadioColumnLinesEnabled(dialogID: LONGINT; componentID: LONGINT; columnIndex: INTEGER): BOOLEAN;
</code>
[[VS:AreLBRadioColumnLinesEnabled]] (only for control type [[#Radio| Radio]]) Checks on/off status of the sub-column lines for a chosen radio column. Note that this applies to a column, not to the whole List Browser as AreLBColumnLinesEnabled.
<code lang="pas">
EnableLBRadioColumnLines(dialogID: LONGINT; componentID: LONGINT; columnIndex: INTEGER;
enableRadioColumnLines: BOOLEAN);
</code>
[[VS:EnableLBRadioColumnLines]] Default: FALSE. (only for control type [[#Radio| Radio]]) Draws extra gray vertical lines for each sub-column in the chosen Radio column. For this to show you need:
* Column with control type [[#Radio| Radio]]
* [[#Column Data Items (list)| List of Column Data Items]]
This is applied on column index basis. Most of the times only a single Radio column is needed in a List Browser, so you'll choose to enable the radio vertical lines at the end of -and outside- the column creation loop (if any).
=== Column Data Items (List) ===
If you're not a programmer -like me-, you'll struggle quite a bit to understand what Data Items are and above all what they do: they are items in a list of data. This list is available to the column where it's defined. I believe that a column can use only ONE list. At least in Vectorscript I could distinguish no access to multiple lists for a single column index.
Whenever you start using List Browsers, soon or later you have the burning wish to coerce cells to a certain list of values. If you could, you'd just love to insert a pull-down menu in your cell, perhaps one with images in it, but this is not possible. You have ''Column Data Items'' instead. These, once defined, are available column-wide. With Column Data Items you can coerce all cells in a column to show only values present in a list. Each list element is a ''Column Data Item''.
[[File:c_LB_ColumnDataItemsImgs.png| right| Column Data Items]] For example you could wish to pair the string "visible" with the black-eye icon. Probably you would like to have more of these items, so you'll define also an item <string "invisible" with the cross icon> and <string "grayed" with the grayed-eye icon>. For each you need to create one ''Data Item'' using [[VS:InsertLBColumnDataItem]] (and you already loaded the icons in the List Browser's [[#Icon (List)| Icon List]] through [[VS:AddListBrowserImage]]).
Not all column's Control Types manage these lists, but for those who do, you will observe that cells using Column Data Items will stop being free and only display a range of Data Items values. For example: "visible, invisible, grayed" and/or show the respective images on user's click.
The sequence in which you create the data items is relevant. ''Column Data Items'' build an indexed list whose order is used by the columns where you apply them. The behavior of your cells depends on the column's [[#Control type| Control Type]]. Moreover is influenced by the chosen column's [[#Display Type| Display Type]]. The only exception is the control [[#Radio| Radio]], which uses the insertion order of the [[#Icon (List)| Icon List]].
; On-click behavior: you can distinguish among column's controls which display their list of Column Data Items on click and those who don't. All cells can be loaded with one ''Column Data Item'' using [[VS:SetLBItemUsingColumnDataItem]], but only cells whose columns belong to [[#Multi State| Multi State]] and [[#Single Instance| Single Instance Icon]] will toggle Data items on click (even if they weren't pre-loaded using [[VS:SetLBItemUsingColumnDataItem]]). Radio columns do this too, but are a world on their own, please see [[#Radio| Radio]].
Column Data Items are to be used only on columns with control type [[#Multi State| Multi State]], [[#Single Instance| Single Instance Icon]] or [[#Radio| Radio]]. And -stating the obvious- you have no reason whatsoever to use these controls without Column Data Items. But in rare circumstances you might find comfortable to store hidden data in Column Data Items just to be able to fetch it on need even from columns whose click-response is not available, like Static.
; Control [[#Multi State| Multi State]] or [[#Single Instance| Single Instance Icon]]: you should always pre-load cells using with this control type with [[VS:SetLBItemUsingColumnDataItem]] in order to have proper start values. Don't use [[VS:SetLBItemInfo]]. This is particularly important for cells whose column has control type Single Instance Icon. Under circumstances you'll like to pre-load an empty image.
; Control [[#Radio| Radio]]: it has the peculiar behavior to load all images starting with the first defined in the [[#Icon (List)| Icon List]], unregarded the icon indexes passed in [[VS:InsertLBColumnDataItem]]. It can only display images ignoring text altogether.
{| class="wikitable" style="margin: auto;"
|-
|-
! colspan="3" | Column Data Items and Control Type
| colspan="3" style="text-align: center;"| Icons, Strings
| rowspan="4" |
* [[#Create_a_List_Browser|  Create a List Browser]]
* [[User:CBM-c-/VS-List_Browsers_part_2#Icons_.28list.29|  Create a List of Icons]] (optional)
* [[User:CBM-c-/VS-List_Browsers_part_2#Columns|  Create columns]]
*: For each column:
*: [[User:CBM-c-/VS-List_Browsers_part_2#Control_Type|  Set Control Type]] (optional)
*: [[User:CBM-c-/VS-List_Browsers_part_2#Display_Type| Set Display Type]] (optional)
*: [[User:CBM-c-/VS-List_Browsers_part_2#Column_Data_Items_.28list.29|  Create a List of Column Data Items]] (optional)


|-
* [[User:CBM-c-/VS-List_Browsers_part_3#Rows_and_Cells| Insert rows]]
! width="150px" | [[#Control_Type| Control Type]] || [[#Display Type| Display Type]] || Response on click
*: For each row loop down all columns
*: and set for each cell:
** [[User:CBM-c-/VS-List_Browsers_part_2#Column_Owner_Type| Column Owner Type]] (optional)
** cell values:
*** string and/or image, or
*** item from a [[User:CBM-c-/VS-List_Browsers_part_2#Column_Data_Items_.28list.29| List of Column Data Items]] (optional)


|-
|-
| 1 [[#Static| Static]]
! colspan="2" | [[User:CBM-c-/VS-List_Browsers_part_2#Column| Column]]
| colspan="2" style="text-align: center;" &lt;doesn't respond&gt;
!  [[User:CBM-c-/VS-List_Browsers_part_3#Rows_and_CellsCell]]


|-
|-
| 2 [[#Radio| Radio]]
! [[User:CBM-c-/VS-List_Browsers_part_2#Control_Type| Control Type]]
|
display HOW
* Image
* Text and Image
 
| Shows multiple choices with icons, creates a sub-column for each call to [[#Column Data Items (list)| Manage the Data Items List]] [[VS:InsertLBColumnDataItem]], although it displays the [[#Icon (List)| Icons]], in a sequence. Upon clicking on a sub-column, the image corresponding to the sub-column index (0-based) will show. Is unable to display text.


|-
! [[User:CBM-c-/VS-List_Browsers_part_2#Display_Type| Display Type]]
| 3 [[#Multi_State| Multi State]]
display WHICH DATA
|
* any


| At each click displays the next Data Item by index in the list.
! [[User:CBM-c-/VS-List_Browsers_part_2#Column_Owner_Type| Column Owner Type]]
display WHAT


|-
|-
| 4 [[#Single_Instance_Icon| Single Instance Icon]]
|  
|  
* any
# [[User:CBM-c-/VS-List_Browsers_part_2#Static|  Static]]
# [[User:CBM-c-/VS-List_Browsers_part_2#Radio|  Radio]]
# [[User:CBM-c-/VS-List_Browsers_part_2#Multi_State|  Multi State]]
# [[User:CBM-c-/VS-List_Browsers_part_2#Single_Instance_Icon|  Single Instance Icon]]
# [[User:CBM-c-/VS-List_Browsers_part_2#Static_icon|  Static icon]]
# [[User:CBM-c-/VS-List_Browsers_part_2#Number|  Number]]
# [[User:CBM-c-/VS-List_Browsers_part_2#Multiple_Icons|  Multiple Icons]]


| Upon clicking in a cell display "item 1" of the ''Data Item list''.
|
Any previous selection will be set to "item 0" in the list. Thus you'll have one single instance of item 1 in a whole column. For this control you need only two Column Data Items, the others don't display.
# Image
# Text only
# Image and Text


|-
|
| 5 [[#Static_Icon| Static Icon]]
:  0. [[User:CBM-c-/VS-List_Browsers_part_3#None|  None]]
| colspan="2" style="text-align: center;"| &lt;doesn't respond&gt;
# [[User:CBM-c-/VS-List_Browsers_part_3#Solid_rect| Solid rect]]
 
# [[User:CBM-c-/VS-List_Browsers_part_3#Dual_solid_rect| Dual solid rect]]
|-
# <i>[[User:CBM-c-/VS-List_Browsers_part_3#Pattern_and_Dual_Pattern| Pattern rect]]</i>
| 6 [[#Number| Number]]
# <i>[[User:CBM-c-/VS-List_Browsers_part_3#Pattern_and_Dual_Pattern| Dual pattern rect]]</i>
| colspan="2" style="text-align: center;"| &lt;doesn't respond&gt;
# [[User:CBM-c-/VS-List_Browsers_part_3#Gradients_and_Images| Gradient or image]]
 
# [[User:CBM-c-/VS-List_Browsers_part_3#Blank| Blank]]
|-
# [[User:CBM-c-/VS-List_Browsers_part_3#Text| Text]]
| 7 [[#Multiple_Icons_.28OBSOLETE.29| Multiple Icons]]
# [[User:CBM-c-/VS-List_Browsers_part_3#Dashed_line| Dashed line]]
| colspan="2" style="text-align: center;" | &lt;doesn't respond&gt;
|}
|}


== Lists Validity Scope ==


==== Manage the Data Items List ====
List Browsers are more than tables of cells, they browse lists. You must imagine them like special containers with built-in lists of data which can be created and used on need. These lists have different validity scope.
 
<code lang="pas">
InsertLBColumnDataItem( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
itemString:STRING; imageOn:INTEGER; imageOff:INTEGER; itemData:LONGINT):INTEGER;
</code>
[[VS:InsertLBColumnDataItem]] Inserts one Data Item in the list for the chosen column. You will repeat the call for each Item to be inserted in your Column Data Items list.
 
; result (0-based): index of the newly inserted Data Item. Most of the times is advisable to store only the start or end value of a Data Item List and use that adding or subtracting needed indexes. You might prefer this approach instead of storing singular Item indexes.
 
; itemString: the string attached to this index of Column Data Item. MUST be defined otherwise the item doesn't insert in the List.
:* [[#Multi State| Multi State]], [[#Single Instance| Single Instance Icon]]: you can decide to create a Data Item containing only strings setting both "imageOn" and "imageOff" to "-1".
:* [[#Radio| Radio]]: no string support, but the string must be nevertheless defined.
 
; imageOn: is an icon index of the [[#Icon (List)| Icon List]], the image to be triggered on user's click. Set to "-1" to make image undefined.
:* [[#Multi State| Multi State]] : the icon shown when selecting a cell. At each further click, the next image in the list will be displayed. When the list has reached the end, the whole begins anew from the start.
:* [[#Single Instance| Single Instance Icon]]: on click shows image index 1. Any other selection shows image index 0.
:* [[#Radio| Radio]]: see dedicated chapter.
 
; imageOff: is an icon index of the [[#Icon (List)| Icon List]], the image to be triggered on deselection. Set to "-1" to make the icon undefined.
:* [[#Radio| Radio]]: set this in alternative to imageOn. You shouldn't have both "imageOn" and "imageOff" defined in a single call to InsertLBColumnDataItem. See [[#Radio| Radio]].
:* [[#Multi State| Multi State]] and [[#Single Instance| Single Instance Icon]]: don't support "imageOff".
 
; itemData: elsewhere called "User data". No idea how to use this. If you set something here it will block toggles, if the Longint passed exceeds the [[#Icon (List)| Icon List]]. See User Data.
 
; Notes: If you don't want to load images pass "-1" to the parameters "imageOn" and "imageOff". Here is to be noted that:
:* [[#Multi State| Multi State]] and [[#Single Instance| Single Instance Icon]] columns: ignore the "-1" and show the last valid image previously defined. If no images has been defined at all, the cells show nothing.
:* [[#Radio| Radio]] columns: show white space
 
* [[#Multi State| Multi State]] and [[#Single Instance| Single Instance Icon]] add the images according to the index passed in the parameter "imageOn". [[#Radio| Radio]] columns add them according to the [[#Icon (List)| Icon List]]'s index corresponding to the count of calls of the routine InsertLBColumnDataItem. This has been damn tough to find out.
(only [[#Radio| Radio]]) If you load the parameter "imageOff" you should leave "imageOn" to "-1". Then it will display the image defined for off. If you define both, you'll have "imageOn" permanently visible both on selection and not. All other control types (supporting Column Data Items) will only use imageOn.
 
<code lang="pas">
GetLBColumnDataItemInfo(dialogID: LONGINT; componentID: LONGINT; columnIndex: INTEGER;
columnDataItemIndex: INTEGER;
VAR itemString:STRING; VAR imageOn: INTEGER; VAR imageOff:INTEGER; VAR itemData: LONGINT): BOOLEAN;
</code>
[[VS:GetLBColumnDataItemInfo]] Query a Data Items List of a column for the values associated to a particular item index.
 
; columnDataItemIndex: the column data item to be queried
; itemString: the item text associated with that data item
; imageOn: the "on" image associated with that data item on selection
; imageOff: the "off" image associated with that data item on deselection
; itemData: the item user data. See User Data
 
<code lang="pas">
FindLBColumnDataItem( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER; itemString:STRING;
VAR columnDataItemIndex:INTEGER):BOOLEAN;
</code>
[[VS:FindLBColumnDataItem]] Queries a Data Items List for a specific string and returns the index of the found Data Item, if any. If no item can be found returns FALSE and sets the variable "columnDataItemIndex" to "-1".
 
 
<code lang="pas">
RemoveLBColumnDataItem( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
columnDataItemIndex:INTEGER):BOOLEAN;
RemoveAllLBColumnDataItems( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER);
</code>
[[VS:RemoveLBColumnDataItem]], [[VS:RemoveAllLBColumnDataItems]] Remove one or all items from a Column Data List.


; List of icons: available to the whole List Browser.


<div style="margin: 1em; border: dotted 1px #aaa; padding: 1em; background-color: #F2F2F2;">
; Column Data Items: available to one column. Column Data Items will often use images from the List Browser's Image list, but they can also contain only strings.


Here words don't help. If you wish to understand the deepness of Column Data Items lists, you must go through some experiments. Get nuts with this:


==== Example: Data Item won't insert because of empty string ====
== Create a List Browser ==


<code lang="pas">
List Browsers are called in a Layout Creation routine with '''CreateLB'''.
{ will be ignored since the string is missing }
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, '', imgIndex, -1, 0);
</code>
 
==== Example: text only toggles ====


<code lang="pas">
<code lang="pas">
{ text only toggles: use Item/Edit Display "Text only" }
CreateLB(dialogID: LONGINT; componentID: LONGINT;  
dataItemCnt1 := InsertLBColumnDataItem(dlog, LB, col, 'Orso', -1, -1, 0);
widthInCharacters: INTEGER; heightInCharacters: INTEGER);
dataItemCnt1 := InsertLBColumnDataItem(dlog, LB, col, 'Atta', -1, -1, 0);
dataItemCnt1 := InsertLBColumnDataItem(dlog, LB, col, 'Ax', -1, -1, 0);
</code>
</code>


==== Example: text/image toggles ====
[[VS:CreateLB]] Creates an empty List Browser with no rows and no columns.


<code lang="pas">
; '''widthInCharacters''', '''heightInCharacters''': is different between Mac and PC. Basically on Mac all dialog items with a scroll bar interpret the width excluding the bar. This will influence the width of dialog elements such as PullDown menus, List Browsers, Lists and such. These items won't align with Static Text, Edit fields and similar: the scroll bar will be outside alignment. You must correct the width programmatically.
{ text/image toggles: use Item/Edit Display "Image and text"}
{ gImgCnt is the count of images loaded in the [[#Icon (List)| Icon List]] }
dataItemCnt3 := InsertLBColumnDataItem(dlog, LB, col, 'image 1', gImgCnt-2, -1, 0);
dataItemCnt3 := InsertLBColumnDataItem(dlog, LB, col, 'image 2', gImgCnt-1, -1, 0);
dataItemCnt3 := InsertLBColumnDataItem(dlog, LB, col, 'image 3', gImgCnt, -1, 0);
</code>


==== Example: only string and one image at start: icon 2 ====


<code lang="pas">
=== List Browsers in resizable dialogs ===
{ emtpy on "Radio", always image 2 on "Multi State" and "Single instance" }
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'string only', 2, -1, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'string only 2', -1, -1, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'string only 3', -1, -1, 0);
</code>


==== Example: targeted index sequence ====
List Browsers are special dialog items with a built-in binding to their parent container. If a dialog is resizable, they resize width and height automatically, without any [[VS:SetEdgeBinding| Edge Binding]]. For this reason you'll prefer to leave List Browsers outside groups: they will fit beautifully to the dialog window without you to bother.


<code lang="pas">
<code lang="pas">
{ having 10 items defined in the [[#Icon (List)| Icon List]] }
dlog := CreateResizableLayout('List Browsers test', TRUE, 'Close', '', TRUE, FALSE);
{ sequence 0, 1, 2, 3 on "Radio" }
{ ... }
{ sequence 8, 9, 6, 5 on "Multi State" }
CreateLB(dlog, cLB, cLBWidth, cLBHeight);
{ sequence 8, 9 on "Single instance" }
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'image 8', 8, -1, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'image 9', 9, -1, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'image 6', 6, -1, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'image 5', 5, -1, 0);
</code>
</code>


==== Example: use imageOff on Radio column ====
<code lang="pas">
{ having 10 items defined in the [[#Icon (List)| Icon List]] }
{ sequence -, 1, 2, 3 on "Radio", with image 9 on sub-col 0 when NOT selected }
{ sequence 0, 0, 0, 0 on "Multi State" (facit: always image 0, perfectly senseless) }
{ sequence 0, 0 on "Single instance" }
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'image 9 on off', -1, 9, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'image 1', 0, -1, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'image 2', 0, -1, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'image 3', 0, -1, 0);
</code>
</div>


== Load a List Browser ==


=== Display Type ===
Your List Browser after creation is just an empty container without columns, rows or any data. Before loading the List Browser it is important to understand what needs to be loaded once and if there are things that needs to be loaded repeatedly, after destroying data. Aside of images, which must be really loaded only once, all other List Browser elements can be loaded, destroyed, created or modified according to your needs. The typical script will set up a List Browser once in [[VS:Creating_a_Custom_Dialog_Box| SetupDialogC]] and manipulate repeatedly only rows data. Usually you will:


The '''Display Type''' determines which type of content a column should show: '''Text only''', '''Image''', or '''Text and Image'''. Image means here '''Icon'''. What you see at the end depends on the column's [[#Control Type| Control Type]], if [[#Column Data Items (list)| Column Data Items]] are setup or not, not last it depends on the [[List Browsers Prt 2#Owner| Owner]] of the singular cell. The Display Type choice applies to all cells in a column. Default upon column creation is ''Text only''.
* add [[#Icon (List)| Icons]] always only once
* add [[#Columns| Columns]] most times once
* add [[#Column Data Items (list)| Column Data Items]] most times only once
* add/delete/modify [[User:CBM-c-/VS-List_Browsers_part_2#Rows and Cells| Rows and Cells]] repeatedly


;Note: The documentation lists ''Item'' and ''Edit'' Display Type. They used to behave differently, but starting with VW 2017 they seem to be fully exchangeable, tested up to VW 2021.
Then you'll organize your loading code as follows:
: Control type Radio is indifferent to both Item and Edit Display: it can only display Images.
: in the documentation the Display Type '''Text Only''' is '''0''', but this is only valid on '''Get'''. Upon '''Set''' the value to be used is '''2'''.


* needs loading once --> has a place in the [[VS:Creating_a_Custom_Dialog_Box| SetupDialogC]] CASE item of your dialog driver routine
* needs loading repeatedly --> resides in a subroutine with wider validity scope (including [[VS:Creating_a_Custom_Dialog_Box| SetupDialogC]] for the first run).


{| class="wikitable" style="margin: auto;"
|-
! Display Type !! Description !! Control Types
|-
| '''1-Image'''
| shows only an image<br>[[File: c_LBdispImg.png]]
| rowspan="3"  |
{| class="wikitable" style="margin: auto;"
|-
! Display Type setting needed
| no setting needed


|-
=== Items and Sub-items, index (0-based) ===
|
: 1 Static
: 6 Number
: 3 Multi State
: 4 Single Instance
: 5 Static Icon
: 7 Multiple Icons
|
: 2 Radio
|}


|-
At start the main difficulty in understanding List Browsers is caused by the naming of the various routine parameters. Everyone will struggle initially with "itemIndex" and "subItemIndex", sometimes also called (more clearly) "columnIndex". The official documentation uses names not consequently. Just some examples:
| '''2-Text only''' (Set), '''0-Text only''' (Get)
| shows only text<br>[[File: c_LBdispTxt.png]]


|-
| '''3-Image and Text'''
| shows both image and text<br>[[File: c_LBdispTxtImg.png]]
|}


<code lang="pas">
<code lang="pas">
{ display type flags: }
GetLBItemInfo(dialogID: LONGINT; componentID: LONGINT; itemIndex: INTEGER; subItemIndex: INTEGER;  
kLBdisplTypeText = 0; { valid for Get! }
VAR itemString: STRING; VAR imageIndex: INTEGER): BOOLEAN;
kLBdispImageOnly = 1;
kLBdispTextOnly = 2; { valid for Set! }
kLBdispImageAndText = 3;


GetLBItemDisplayType( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER):INTEGER;
GetLBItemData(nDialogID, nComponentID :LONGINT; nItemIndex, nSubItemIndex :INTEGER;  
GetLBEditDisplayType( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER):INTEGER;
VAR nUserData :LONGINT);


SetLBItemDisplayType( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;  
InsertLBColumn( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
displayType:INTEGER):BOOLEAN;
headerString:STRING; width:INTEGER):INTEGER;
SetLBEditDisplayType( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
displayType:INTEGER):BOOLEAN;
</code>
</code>
[[VS:GetLBItemDisplayType]], [[VS:GetLBEditDisplayType]], [[VS:SetLBItemDisplayType]], [[VS:SetLBEditDisplayType]]
Item and Edit Display type seem to be exactly the same. Radio columns ignore them.
<div style="margin: 1em; border: dotted 1px #aaa; padding: 1em; background-color: #F2F2F2;">
==== Example: item/edit display type ====
We need to observe what happens changing Item or Edit Type (nothing, as of VW 2021). Two pull-down menus allow you to experiment with these settings. After you change the values, the List Browser will refresh. For this reason we set these values in a sub-routine that will be called on demand: "LB_LoadColumns".
<code lang="pas">
FOR col:=1 TO GetNumLBColumns(dlog, LB) -1 DO BEGIN
temp_b := SetLBItemDisplayType(dlog, LB, col, itemType);
temp_b := SetLBEditDisplayType(dlog, LB, col, editType);
{ ... }
END;
</code>
</div>
=== Control Type ===


The '''Control Type''' determines how a column will react to the user. Nevertheless, in order to achieve the desired effect, you must combine control types with the appropriate [[#Display Type| Display Type]] (text only, icon only, text and icon). The Control Type and related Display Type choice depends on the kind of content that you mean to display in the column: picking from a List of Column Data Item or not. The default control type upon column creation is "1" = '''Static'''.
[[VS:GetLBItemInfo]], [[VS:GetLBItemData]], [[VS:InsertLBColumn]] Manage the count of columns.


It's not always necessary to set a control type, only when the default value Static is not sufficient, that is, whenever you need special facilities for using a List of Column Data Items. The control type can be changed after column insertion using '''SetLBControlType'''.
;  row index: itemIndex, nItemIndex
; column index: subItemIndex, nSubItemIndex, columnIndex
If you see both parameters for row and for column you know that you'll target a cell (intersection of row and column).


All indexes in List Browsers are 0-based:


{| class="wikitable" style="margin: auto;"
* [[#Icon (List)| Icons]]
|-
* [[#Column Data Items (list)| Column Data Items]]
! Control Type !! [[#Display Type| Display Type]] !! needs !! is good for
* [[#Columns| Columns]]
|-
* [[#Rows and Cells| Rows and Cells]]
| 1- [[#Static| Static]] || required || - || Text, simple icons, color cells, line style cells, thumbnail cells
|-
| 2- [[#Radio| Radio]] || ignores it || [[#Column Data Items (list)| Column Data Items]] ||''' Multiple options''' with clickable icons. Can only do this.
|-
| 3- [[#Multi State| Multi State]] || required || [[#Column Data Items (list)| Column Data Items]] || '''Multiple toggles on click''' of column data items, otherwise like Static
|-
| 4- [[#Single Instance Icon| Single Instance Icon]] || required || Two [[#Column Data Items (list)| Column Data Items]] || '''On-off toggle on click''' of two column data items, otherwise like Static
|-
| 5- [[#Static Icon| Static Icon]] || required || - || '''Icon from column data items''', otherwise like Static
|-
| 6- [[#Number| Number]] || required || - || '''Numbers''', otherwise like Static
|-
| 7- [[#Multiple Icons| ultiple Icons]] || required || Icon Resources from external file || '''Multiple icons''' in one cell, without preloading in the [[#Icon (List)| Icon List]], otherwise like Static (Mac only)
|}


<code lang="pas">
This implies that for setting the absence of an image -for example- you need to pass "-1". Take care to init your variables to -1. Mind the routine AddLBImage which on Mac might return "0" also if it failed to insert an image.
{ control type flags }
kLBctrNone = 1; { Static }
kLBctrRadio = 2;
kLBctrMultiState = 3;
kLBctrSingleInstance = 4;
kLBctrNoClick = 5; { Static icon }
kLBctrNumber = 6;
kLBctrMultipleImages = 7; { Use with kListBrowserDisplayImageOnly }
kLBctrDiscTriangl = 8; { introduced by VW 17 (2012) VW 18 (2013) }
temp_b := SetLBControlType(dlog, LB, colNr, controlType);
</code>
[[VS:SetLBControlType]] Sets the behaviour of all cells in a column.  


; result: returns FALSE if
Many mistakes in List Browsers are caused by unwanted 0-indexes in images or Column Data Items.
:* no columns are present in the List Browser
:* you try to set the control type on a column whose control is already set to the value "controlType" passed in the routine. For this reason is not advisable to code depending on the TRUE result of this routine. For example: set the Display Type if SetLBControlType returns TRUE. It will fail to change the display simply because the control was already set as you wish. Just assign the result of SetLBControlType to a temporary boolean variable which you'll trash.


; controlType: use one of the constants below. It is advisable to store them in an external px file to be loaded as include whenever a dialog involves List Browsers.
:* kLBctrNone = 1; { Static }
:* kLBctrRadio = 2;
:* kLBctrMultiState = 3;
:* kLBctrSingleInstance = 4;
:* kLBctrNoClick = 5; { Static icon }
:* kLBctrNumber = 6;
:* kLBctrMultipleImages = 7; { Use with kListBrowserDisplayImageOnly }
:* kLBctrDiscTriangl = 8; { introduced by VW 17 (2012) VW 18 (2013) }


; Note; the three ''Control Types'' '''Radio''', '''Multi State''', '''Single Instance Icon''' only work properly if associated to a List of [[#Column Data Items (list)| Column Data Items]].
== Icon (List) ==


Inside the dialog event handling these types don't even return an active control without a list, not even the expected "-1". Experiment with the generic routine [[VS:GetActiveEditItem]] passing the dialog ID, try with and without Column Data Items associated to the column.  
Very relevant for the layout of a List Browser is the list of icons (optional). Here you store small icons that will be available to the whole List Browser. The icons must be either loaded from external resource files or recycled from the built-in Vectorworks icons. They can be used directly from the singular cells and/or be used to build data in the List of Data Items of a column. An [[#Icon (List)| Icon List]] can be created using '''AddListBrowserImage'''.  


There is a single routine allowing to load icon resources without pre-loading them in the [[#Icon (List)| Icon List]] first: '''SetLBImageIndexes''', which applies only to columns with control type Multiple Icons.


==== Static ====
To my knowledge, once loaded the original indexes of the icon-resources are lost: for example it's not possible to fetch the original resource index of icon "2" in the '''Icon List''' and substitute it with something else. This is only possible on Mac using columns of type Multiple Icons when they are loaded with '''SetLBImageIndexes''', but I didn't try this with the new routine, only with its precursor: SetLBMultImageIndexes (obsolete).


[[File:c_LB_controlStatic.png| right]] Static is the default control upon column creation. Sometimes also called ''None'' (not to be mistaken for the [[User:CBM-c-/VS-List_Browsers_part_3#Column Owner Type| Cell Owner None]]). It displays well anything excluding toggles (Column Data Items). In fact it can display them too, but only statically, no toggle on user's click, thus its name, I suppose. Using Data Items here make this control nearly identical to Static Icon.
It's also not possible to delete the image-list from the List Browser, once created. For this reason images must be loaded only once In [[VS:Creating_a_Custom_Dialog_Box| SetupDialogC]] and attention be paid that '''AddListBrowserImage''' doesn't land in any repetitive routine: this would add the same image over and over again, each time increasing the index count.


This control is the good choice for '''text''', simple '''icons''', resource '''thumbnails''', '''colored cells''', '''line styles'''. Numbers should instead be created with a control type '''Number''', since that offers a few more targeted facilities.
The sequence defined by adding images is not relevant if you need the icons for singular cells or for a List of Data Items, but becomes very important if your icons need to be displayed in Radio columns, since this control follows the insertion order of the [[#Icon (List)| Icon List]] precisely.


:; For this to work you need: nothing. But a few observations can be made as to the display:
:* [[#Display Type| Display Type]] '''Image''': shows an icon (if loaded in the [[#Icon (List)| Icon List]]) or attribute cell
:* [[#Display Type| Display Type]] '''Text''': shows only text (really no good for attribute cells: infamous "AAAAA string" if you didn't set the cell's owner at the right place of your code)
:* [[#Display Type| Display Type]] '''Text and Image''' : shows both icon and text (even worse for attribute cells)
Use it for columns whose cells have the following [[User:CBM-c-/VS-List_Browsers_part_3#Column Owner Type| Owner Types]]:
* '''None''' (default) --> load cells with [[VS:SetLBItemInfo]]
* '''Text''' --> load cells with [[VS:SetLBItemInfo]]
* '''Solid Rect''' and '''Dual solid rect''' --> load cells with the [[User:CBM-c-/VS-List_Browsers_part_3#Color Cells| Color routines]]
* '''Gradients and Images''' --> load cells with [[User:CBM-c-/VS-List_Browsers_part_3#Indexed Resources| Indexed Resources Routines]]
* '''Dashed line''' --> load cells with [[User:CBM-c-/VS-List_Browsers_part_3#Color Cells| Color]] and [[User:CBM-c-/VS-List_Browsers_part_3#Line Styles| Line Styles routines]]
==== Radio ====
[[File:c_LB_controlRadio.png| right]]Used to display multiple choices as sub-columns of icons, triggered on click. Compared to the other controls, it's a radical modifier with following peculiarities:
* ignores any string (expected).
* ignores Display Types (expected).
* displays icons always according to the sequence defined in the [[#Icon (List)| Icon List]], ignoring the indexes passed in the parameter "imageOn" in the Data Items List. For each sub-column to be created a call to [[VS:InsertLBColumnDataItem]] must be performed. Each call matches the sub-column to the corresponding index from the [[#Icon (List)| Icon List]]. It means that you cannot "shuffle" the order set in the [[#Icon (List)| Icon List]] and must plan the insertion order of the icons in the [[#Icon (List)| Icon List]] carefully. This behavior is unique among Column Data Item aware control types.
:; For this to work you need:
:* [[#Icon (List)| Icon List]] : at least two images in the list.
:* [[#Column Data Items (list)| Column Data Items]] : create two or more items.
:* [[List Browsers Prt 2#Owner| Cell Owner]]: <anything>: is always ignored.
:* Use [[VS:SetLBItemUsingColumnDataItem]] for loading cell values, not [[VS:SetLBItemInfo]]
:* Use [[#Column Lines| Column Lines]] for setting up optional ''Radio Column Lines'' for the sub-columns
<code lang="pas">
InsertLBColumnDataItem( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
itemString:STRING; imageOn:INTEGER; imageOff:INTEGER; itemData:LONGINT):INTEGER;
</code>
[[VS:InsertLBColumnDataItem]] Inserts one sub-column in the Radio column. You will repeat the call for each Column Data Item to be inserted in your list. Each created sub-column corresponds to the same index of the [[#Icon (List)| Icon List]].
For example: passing icon index "5" in the first call to '''InsertLBColumnDataItem''' won't load that icon in the first sub-column. The icon displayed on click will be index "0" (first item in the [[#Icon (List)| Icon List]]).
; itemString: is ignored but must be defined, otherwise the sub-column doesn't generate.
; imageOn: is an icon stored in the [[#Icon (List)| Icon List]]: the icon to be shown on column selection. The index passed to "imageOn" can be anything within the range "0 - countOfIcons" in the [[#Icon (List)| Icon List]]. Set to "-1" to make it undefined.
; imageOff: is an icon stored in the [[#Icon (List)| Icon List]]: the icon to be shown upon de-selection. Set to "-1" to make it undefined. Use "imageOff" only as alternative to '"imageOn". You shouldn't have both "imageOn" and "imageOff" defined in a single call to InsertLBColumnDataItem otherwise "imageOn" displays permanently. Using "imageOff" instead of "imageOn" it will display "imageOff" on deselection.
===== Evil Example - cryptical subcolumns count =====
<div style="margin: 1em; border: dotted 1px #aaa; padding: 1em; background-color: #F2F2F2;">
How many sub-columns does this generate? And what do they do?


<code lang="pas">
<code lang="pas">
{ sub-col 0 } dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'imageOn-0', gImg, -1, 0);
AddListBrowserImage(dialogID: LONGINT; listBrowserID: LONGINT; imageSpecifier: DYNARRAY[] of CHAR): INTEGER;
{ sub-col 1 } dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'no images', -1, -1, 0); { string only }
{ sub-col 2 } dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, '', gImg+2, -1, 0); { will be ignored: no string }
{ sub-col 3 } dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'imageOn-1', gImg +1, -1, 0);
{ sub-col 4 } dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'imageOff-1', -1, gImg+1, 0);
{ sub-col 5 } dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'imageOn-0', gImg, -1, 0);
{ sub-col 6 } dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, '', -1, -1, 0); { will be ignored: no string }
{ sub-col 7 } dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'imageOff-2', -1, gImg+2, 0);
{ sub-col 8 } dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, '', gImg+1, -1, 0); { will be ignored: no string }
{ sub-col 9 } dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'no images', -1, -1, 0); { string only }
</code>
</code>
[[VS:AddListBrowserImage]] Adds an image to the list of images that a list browser can use.


Passing the same imageOn over and over still make the Radio column display the full Icon List sequence:
; imageSpecifier: the path to the image resource (since VW 2104), for example: 'Vectorworks/ResourceBrowser/WallStyle.png'.
: I prefer to use icons from the application self, where there is a very large choice: loading resources from Vectorworks spares you the creation of your own resource file[s]. The application ships with a huge number of icons and you always find something that fits your needs.


<code lang="pas">
; result (0-based): the index of the newly inserted icon. This increases at each newly inserted icon. Once added, images cannot be deleted and stay with the List Browser until script ends. For this reason Images should be added to the List Browser only once. This is best done in the [[VS:Creating_a_Custom_Dialog_Box| SetupDialogC]] section of the dialog driver.
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'try0', 2, -1, 0);
: Since the index is 0-based you should remember later to use "-1" for telling cells not to use images. Whenever you see a routine allowing for an image index (for example [[VS:SetLBItemInfo]]) and you don't want an image to load, set the image index parameter to "-1". A typical mistake is to write "0", which will load the first image in the LB, if any loaded. If you didn't load images into your LB previously, you don't need to bother much.
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'try1', 2, -1, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'try2', 2, -1, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'try3', 2, -1, 0);
</code>
</div>




==== Multi State ====
=== Use custom icons ===


[[File:c_LB_controlMultiState.png| right]] For '''Multi State''' you won't notice the difference until a [[#Column Data Items| List of Column Data Items]] is used, in absence of which it behaves like '''Static'''. If Column Data Items are loaded in form of string or images (or both) the cell will loop down all items at each user's click. Multi State justifies the name "List Browser": it browses the list (... of Column Data Items). You'll use it when you wish to restrict the available choices to a controlled range.
* Create icons of size 16/16 px with a resolution of 72dpi.
* Save them as .png file
* Place them in the same folder where you have your plug-in .vsm file)


Proper Column Data Items should be pre-loaded in each cell using [[VS:SetLBItemUsingColumnDataItem]] in the row creation routine.
See example below to access them.


:; For this to work you need:
:* [[#Column Data Items| Column Data Items]]: must be set to two or more items. You can toggle strings and images. At each user's click the cell changes to the next item in the list.
:* [[#Display Type| Display Type Image]]: toggles images starting from the index position currently valid in the cell
:* [[#Display Type| Display Type Text]]: like above, but toggles text strings
:* [[#Display Type| Display Type Text and Image]]: like above, but toggles both text and images
:* [[User:CBM-c-/VS-List_Browsers_part_3#Column Owner Type| Cell Owner None]] (default) is a good choice
:* Use [[VS:SetLBItemUsingColumnDataItem]] for loading cells, not [[VS:SetLBItemInfo]]


=== Use icons from Vectorworks.app ===


==== Single Instance Icon ====
You can use all images shipped in VectorworksXX.app. There are also images stored in the SDK libraries provided by the various plug-ins:
* on Mac
** right-click on the application icon Vectorworks 20xx.app or any .vwlibrary file from the Plug-in Folder
** select '''Show Package Contents'''
** navigate to /Contents/Resources/xxx.vwr/Images
** use the file path name without @2x and suffix
* on Win (to do)


[[File:c_LB_controlSingleInstIcon.png| right]] '''Single Instance''' is another [[#Column Data Items| Column Data Items]]' toggle. Also behaves as '''Static''' if no Data Items are used. Seems bizarre at first. You'll use it when you need a single instance of data to be present in a whole column, for example an icon marking the currently -only- active layer. Unregarded the definition, you can also use if for text strings. A NNA example of its usage can be seen in the Organization Dialog > Classes, when the option "Details" is on: is the selection icon on the left of the class names.
It has a built-in '''Single line Selection''' enabled: you can't select multiple rows when you click on a cell belonging to a Single Instance column. As soon as you click on a cell it will set the value to the next item defined in the Items list of the parent column. By clicking, the cell previously selected will be assigned the first Item.
:; For this to work you need:
:* [[#Column Data Items| Column Data Items]]: must be set to at least two items. You can toggle strings and images. Any Data Item after index "1" (the second in the list) will be ignored.
:* [[#Display Type| Display Type Image]]: toggles images from index "0" to "1"
:* [[#Display Type| Display Type Text]]: like above, but toggles text strings
:* [[#Display Type| Display Type Text and Image]]: like above, but toggles both text and images
:* [[User:CBM-c-/VS-List_Browsers_part_3#Column Owner Type| Cell Owner None]] (default) is a good choice
:* Use [[VS:SetLBItemUsingColumnDataItem]] for loading cells, not [[VS:SetLBItemInfo]]
This will only work well if you really make sure to init the cells to proper values. The problem is when there wasn't a previous selection (just after launch for example) and the cells are set to a value whose appearance is not that of the Column Data Items: all cells in the Single Instance column should be initialized to the 1st Column Data Item, but one, which will get the 2nd.
Since you know that all cells in the Single Instance column will (should) have the same value but one, you can search it securely using [[VS:FindLBColumnItem]].
==== Static Icon ====
[[File:c_LB_controlStaticIcon.png| right]] '''Static Icon''' is a [[#Column Data Items| Column Data Items]]' control. As usual behaving like '''Static''' if no '''Data Items''' are used. This control is sometimes called '''No click''', which in this case is a good hint to the functionality that it triggers in its cells: it displays a single Data Item without allowing for any user interaction on click. As a whole it behaves absolutely the same as Static when passing to it a Data Item index (no click toggles).
The only difference that I could discover is that Static Icon cells send on click an event "-2" which I believe to be indicative of "Data-Items-awareness" while Static won't ever do it. The finesse of this control type is too much for me and I never used it. I'd love to know more about it.
:; For this to work you need:
:* see [[#Static| Static]]
:* Use [[VS:SetLBItemUsingColumnDataItem]] for loading cells, not [[VS:SetLBItemInfo]]
==== Number ====
[[File:c_LB_controlNumber.png| right]] '''Number''' is a column control mainly dedicated to '''Drag and Drop'''. Otherwise it feels a little underdeveloped, above all because it won't sort cells numerically (or I didn't find out how). "400" will appear before "70", typical of alphabetical sorting.
It has a feature: it will strip out leading zeros from strings, if applicable. For example "0006" turns into "6" and will nevertheless sort as 0006. This opens the workaround to pad strings with a large amount of zeros at start and bring all numbers in a column to have the same characters' length. At this point you can sort also alphabetically and it will look like a numerical sorting.
And to my knowledge this is the only way of letting the columns sort "numerically". If anyone knows the straight way I beg to be informed.
Owner Number also doesn't automatically align left cell content. You must align the singular cells with SetLBItemTextJust. It doesn't do math: passing string "7-6" doesn't return "1", but this shouldn't be expected.
:; For this to work you need:
:* [[User:CBM-c-/VS-List_Browsers_part_3#Column Owner Type| Cell Owner Text]]
:* [[#Display Type| Display Type Text]]
:* Use [[VS:SetLBItemInfo]] for loading cells
Anything else is just wilderness [... of pain]. See the image on the right: only the "Owner Text" reliably displays a number. And this with Edit Display Image.
==== Multiple Icons ====
[[File:c_LB_controlMultiIcons.png| right]] '''Multiple Icons''' is sometimes also called '''Multiple Images'''. This control can display up to three images at the same time in a cell. The icon resources can be loaded directly from a resource file and don't need to be preloaded in the [[#Icon (List)| Icon List]]. It doesn't react on user's click and is not operational with '''Column Data Items''' (whereby it sends an event "-2", which is indicative of some Data Items awareness, but I didn't succeed in discovering which). Is an image-based control type which expects Edit Display.
A NNA example of Multiple Icons can be seen in the Organization dialog > Saved Views > Class Options, when the "Visibility" option is activated.
This control type behaves differently between Mac and PC:
Note: the info below must be checked for VW 2021:
:; PC: Doesn't display any image whatsoever. Whereby I am tempted here to file a VectorScript bug, since according to the SDK the corresponding callback routine '''SetListBrowserMultImageIndexes''' is implemented for both platforms. Which leaves the error to rely only on the control type Multiple Icons. And there are examples on the Organization dialog which work flawlessly on both platforms.
:; MAC: Allows to load directly three images from a resource file without adding them previously to the List of icons through '''AddListBrowserImage'''.
:; For this to work you need: ... a Mac, then
:* [[User:CBM-c-/VS-List_Browsers_part_3#Column Owner Type| Cell Owner None]]  (default) is a good choice
:* Image Paths: the access is the same as for Add Images. All three images must be set, otherwise nothing display. You cannot pass -1 to any of the variables, but you can pass "0" which will make the image blank (since at index "0" of the built-in resources there is no image loaded).
:* [[#Display Type| Display Type Image]] shows only three images.
:* [[#Display Type| Display Type Image and text]]: shows three images and the first Column Data Item defined if any, or the text set in the rows info. Mind, this is not a control that seems to be interesting with Column Data Items.
Studying this routine a little, it is evident that it offers a fast and most flexible switch, since you don't have to bother with pre-loading the images. Too bad that it doesn't work on PC.
==== Example: column set-up ====


=== Example: Load icons for a visible/grayed/invisible toggle ===


<div style="margin: 1em; border: dotted 1px #aaa; padding: 1em; background-color: #F2F2F2;">
<div style="margin: 1em; border: dotted 1px #aaa; padding: 1em; background-color: #F2F2F2;">
Create nine columns. Titles are stored in an array of string "gTitles" defined elsewhere. The default column width is "90".
<code lang="pas">
<code lang="pas">
{ first column is of type 6 = Number }
{ add three images to a List Browser and index them: }
col := InsertLBColumn(dlog, LB, GetNumLBColumns(dlog, LB), '#', 40);
imgCnt := -1; { init }
temp_b := SetLBControlType(dlog, LB, col, 6);
imgVis := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Visible');
{ display type: not defined, defaults is 2 = text, OK for number columns }
imgInvis := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Invisible');
imgGray := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Gray');
{ enable drag and drop, this will only work on a column of type Number }
IF EnableLBDragAndDrop(dlog, LB, TRUE) THEN BEGIN
temp_b := SetLBDragDropColumn(dlog, LB, col);  
temp_b := SetLBColumnHeaderJust(dlog, LB, col, 2); { justify title center }
END;


{ Alternatively, store only the total count. Retrive them by offset to the count: }
imgCnt := -1; { init }
imgCnt := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Visible');
imgCnt := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Invisible');
imgCnt := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Gray');


{ loop to add seven other columns }
{ Or use your own icons : }
FOR n:=1 TO 7 DO BEGIN
{ fetch the file path to the .vsm file }
col := InsertLBColumn(dlog, LB, GetNumLBColumns(dlog, LB), gTitles[n], 90);
IF (GetPluginInfo(pioName, pioRecordHandle)) & FindFileInPluginFolder(Concat(pioName, '.vsm'), pluginPath) THEN
{ control type: not defined, defaults is 1 = Static, OK for me }
path2img := Concat(pluginPath, 'Imgs'); { path to a folder named 'Imgs' in your Plug-in folder }
{ display type: not defined, defaults is 2 = Text only, OK for me }
temp_b := SetLBColumnHeaderToolTip(dlog, LB, col, gTitles[n], ''); { add title as column tip }
{ ... }
{ ... }
END;


{ retrive your custom images during SetupDialogC }
imgCnt := -1; { init }
imgCnt := AddListBrowserImage(dlog, LB, Concat(path2img, '/Visible.png'));
imgCnt := AddListBrowserImage(dlog, LB, Concat(path2img, '/Invisible.png'));
imgCnt := AddListBrowserImage(dlog, LB, Concat(path2img, '/Gray.png'));
</code>


{ last column is of type 2 = Radio }
[[File:c_LB_toggleImgs.png| left| Toggle Images]]
col := InsertLBColumn(dlog, LB, GetNumLBColumns(dlog, LB), 'Radio title', 100);
temp_b := SetLBControlType(dlog, LB, col, 2);
{ Display Type undefined, is irrelevant for Radio columns }
 
{ create a list of data items for this col, the icons are already in the LB Icon List }
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'visible', 0, -1, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'invisible', 0, -1, 0);
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, 'grayed', 0, -1, 0);
 
{ zero ImageON might be confusing, but is enough for Radio columns,
they just need a call to InsertLBColumnDataItem and will create
the icons in the order where you added them to the List Browser }
 
EnableLBRadioColumnLines(dlog, LB, col, TRUE);


EnableLBColumnLines(dlog, LB, TRUE); { enable gray lines between columns }
Since we know that the list starts at zero and the counter always increases by 1, we store the final index in the variable "imgCnt". If something goes wrong, we have the needed "-1" value to declare the lack of images. To access them later we'll only need to subtract to this variable. You might prefer to access indexes in this fashion whenever your script uses only few images.
SetLBSortColumn(dlog, LB, 0, TRUE);
{ sorting is on by default, no need to enable it, just enable the column 0 }
</code>
</div>
</div>


{| class="wikitable" style="margin: auto;"
{| class="wikitable" style="margin: auto;"

Revision as of 14:28, 1 January 2021


Some of you know me as Orso from the now defunct Vectorlab, I maintained it for many years. Here is the List Browsers article from the former Vectorlab site. For better understanding please use the comprehensive example at Dialog with List Browser. By --_c_ (talk) 01:31, 31 December 2020 (EST), previously Orso.B.Schmid, 2009-2018.


List Browsers

List Browsers allow for great layout, advanced data management, but also amazing layout features within the single cells. Is not difficult to load them with some text data and make them display a whatever list. Still the advanced settings of List Browsers are quite enigmatic. After years of trial and error, I attempt here to resume what I discovered. If you find an error, I'd be glad to be informed. I am a user and had no reference literature for this article which is based on pure experience, if you are a programmer you will probably find the terms employed not quite the right ones.

Since List Browsers are so complex, the best way to learn how to use them is to have some free code available for experiments. Free code for List Browsers can be counted on one hand and none of the snippets available tells you a single thing about Control Types, Edit Display Types, Item Display Types, Column Data Items and so on. Use the example Dialog with List Browser. It should help experiencing the different List Browser display according to the available options.

For those wishing to deepen the topic: see NSTableView Class (for the MacOs environment).

You'll find a large amount of sub-routines for List Browsers in the page Subroutines - List Browsers (to do).


Part 1: Creation Part 2: Columns Part 3: Rows and Cells Part 4: Events

Introduction

The display of List Browsers is determined by the right combination of four settings:

  • Control Type
  • Display Type
  • Cell's Column Owner
  • Column Data Items

This is all -pardon my French- bloody difficult, mostly because it is not documented. It resembles a 4D-puzzle.


Column Data Items List Browser set up
Icons, Strings
Column Cell
Control Type

display HOW

Display Type

display WHICH DATA

Column Owner Type

display WHAT

  1. Static
  2. Radio
  3. Multi State
  4. Single Instance Icon
  5. Static icon
  6. Number
  7. Multiple Icons
  1. Image
  2. Text only
  3. Image and Text
0. None
  1. Solid rect
  2. Dual solid rect
  3. Pattern rect
  4. Dual pattern rect
  5. Gradient or image
  6. Blank
  7. Text
  8. Dashed line

Lists Validity Scope

List Browsers are more than tables of cells, they browse lists. You must imagine them like special containers with built-in lists of data which can be created and used on need. These lists have different validity scope.

List of icons
available to the whole List Browser.
Column Data Items
available to one column. Column Data Items will often use images from the List Browser's Image list, but they can also contain only strings.


Create a List Browser

List Browsers are called in a Layout Creation routine with CreateLB.

CreateLB(dialogID: LONGINT; componentID: LONGINT; 
	widthInCharacters: INTEGER; heightInCharacters: INTEGER);

VS:CreateLB Creates an empty List Browser with no rows and no columns.

widthInCharacters, heightInCharacters
is different between Mac and PC. Basically on Mac all dialog items with a scroll bar interpret the width excluding the bar. This will influence the width of dialog elements such as PullDown menus, List Browsers, Lists and such. These items won't align with Static Text, Edit fields and similar: the scroll bar will be outside alignment. You must correct the width programmatically.


List Browsers in resizable dialogs

List Browsers are special dialog items with a built-in binding to their parent container. If a dialog is resizable, they resize width and height automatically, without any Edge Binding. For this reason you'll prefer to leave List Browsers outside groups: they will fit beautifully to the dialog window without you to bother.

dlog := CreateResizableLayout('List Browsers test', TRUE, 'Close', '', TRUE, FALSE);
{ ... }
CreateLB(dlog, cLB, cLBWidth, cLBHeight);


Load a List Browser

Your List Browser after creation is just an empty container without columns, rows or any data. Before loading the List Browser it is important to understand what needs to be loaded once and if there are things that needs to be loaded repeatedly, after destroying data. Aside of images, which must be really loaded only once, all other List Browser elements can be loaded, destroyed, created or modified according to your needs. The typical script will set up a List Browser once in SetupDialogC and manipulate repeatedly only rows data. Usually you will:

Then you'll organize your loading code as follows:

  • needs loading once --> has a place in the SetupDialogC CASE item of your dialog driver routine
  • needs loading repeatedly --> resides in a subroutine with wider validity scope (including SetupDialogC for the first run).


Items and Sub-items, index (0-based)

At start the main difficulty in understanding List Browsers is caused by the naming of the various routine parameters. Everyone will struggle initially with "itemIndex" and "subItemIndex", sometimes also called (more clearly) "columnIndex". The official documentation uses names not consequently. Just some examples:


GetLBItemInfo(dialogID: LONGINT; componentID: LONGINT; itemIndex: INTEGER; subItemIndex: INTEGER; 
	VAR itemString: STRING; VAR imageIndex: INTEGER): BOOLEAN;

GetLBItemData(nDialogID, nComponentID :LONGINT; nItemIndex, nSubItemIndex :INTEGER; 
	VAR nUserData :LONGINT);

InsertLBColumn( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER;
	headerString:STRING; width:INTEGER):INTEGER;

VS:GetLBItemInfo, VS:GetLBItemData, VS:InsertLBColumn Manage the count of columns.

row index
itemIndex, nItemIndex
column index
subItemIndex, nSubItemIndex, columnIndex

If you see both parameters for row and for column you know that you'll target a cell (intersection of row and column).

All indexes in List Browsers are 0-based:

This implies that for setting the absence of an image -for example- you need to pass "-1". Take care to init your variables to -1. Mind the routine AddLBImage which on Mac might return "0" also if it failed to insert an image.

Many mistakes in List Browsers are caused by unwanted 0-indexes in images or Column Data Items.


Icon (List)

Very relevant for the layout of a List Browser is the list of icons (optional). Here you store small icons that will be available to the whole List Browser. The icons must be either loaded from external resource files or recycled from the built-in Vectorworks icons. They can be used directly from the singular cells and/or be used to build data in the List of Data Items of a column. An Icon List can be created using AddListBrowserImage.

There is a single routine allowing to load icon resources without pre-loading them in the Icon List first: SetLBImageIndexes, which applies only to columns with control type Multiple Icons.

To my knowledge, once loaded the original indexes of the icon-resources are lost: for example it's not possible to fetch the original resource index of icon "2" in the Icon List and substitute it with something else. This is only possible on Mac using columns of type Multiple Icons when they are loaded with SetLBImageIndexes, but I didn't try this with the new routine, only with its precursor: SetLBMultImageIndexes (obsolete).

It's also not possible to delete the image-list from the List Browser, once created. For this reason images must be loaded only once In SetupDialogC and attention be paid that AddListBrowserImage doesn't land in any repetitive routine: this would add the same image over and over again, each time increasing the index count.

The sequence defined by adding images is not relevant if you need the icons for singular cells or for a List of Data Items, but becomes very important if your icons need to be displayed in Radio columns, since this control follows the insertion order of the Icon List precisely.


AddListBrowserImage(dialogID: LONGINT; listBrowserID: LONGINT; imageSpecifier: DYNARRAY[] of CHAR): INTEGER;

VS:AddListBrowserImage Adds an image to the list of images that a list browser can use.

imageSpecifier
the path to the image resource (since VW 2104), for example: 'Vectorworks/ResourceBrowser/WallStyle.png'.
I prefer to use icons from the application self, where there is a very large choice: loading resources from Vectorworks spares you the creation of your own resource file[s]. The application ships with a huge number of icons and you always find something that fits your needs.
result (0-based)
the index of the newly inserted icon. This increases at each newly inserted icon. Once added, images cannot be deleted and stay with the List Browser until script ends. For this reason Images should be added to the List Browser only once. This is best done in the SetupDialogC section of the dialog driver.
Since the index is 0-based you should remember later to use "-1" for telling cells not to use images. Whenever you see a routine allowing for an image index (for example VS:SetLBItemInfo) and you don't want an image to load, set the image index parameter to "-1". A typical mistake is to write "0", which will load the first image in the LB, if any loaded. If you didn't load images into your LB previously, you don't need to bother much.


Use custom icons

  • Create icons of size 16/16 px with a resolution of 72dpi.
  • Save them as .png file
  • Place them in the same folder where you have your plug-in .vsm file)

See example below to access them.


Use icons from Vectorworks.app

You can use all images shipped in VectorworksXX.app. There are also images stored in the SDK libraries provided by the various plug-ins:

  • on Mac
    • right-click on the application icon Vectorworks 20xx.app or any .vwlibrary file from the Plug-in Folder
    • select Show Package Contents
    • navigate to /Contents/Resources/xxx.vwr/Images
    • use the file path name without @2x and suffix
  • on Win (to do)


Example: Load icons for a visible/grayed/invisible toggle

{ add three images to a List Browser and index them: }
imgCnt := -1; { init }
imgVis := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Visible');
imgInvis := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Invisible');
imgGray := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Gray');

{ Alternatively, store only the total count. Retrive them by offset to the count: }
imgCnt := -1; { init }
imgCnt := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Visible');
imgCnt := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Invisible');
imgCnt := AddListBrowserImage(dlog, LB, 'Vectorworks/Standard Images/Gray'); 

{ Or use your own icons : }
{ fetch the file path to the .vsm file }
IF (GetPluginInfo(pioName, pioRecordHandle)) & FindFileInPluginFolder(Concat(pioName, '.vsm'), pluginPath) THEN
	path2img := Concat(pluginPath, 'Imgs'); { path to a folder named 'Imgs' in your Plug-in folder }
	{ ... }

{ retrive your custom images during SetupDialogC }
imgCnt := -1; { init }
imgCnt := AddListBrowserImage(dlog, LB, Concat(path2img, '/Visible.png'));
imgCnt := AddListBrowserImage(dlog, LB, Concat(path2img, '/Invisible.png'));
imgCnt := AddListBrowserImage(dlog, LB, Concat(path2img, '/Gray.png')); 
Toggle Images
Toggle Images

Since we know that the list starts at zero and the counter always increases by 1, we store the final index in the variable "imgCnt". If something goes wrong, we have the needed "-1" value to declare the lack of images. To access them later we'll only need to subtract to this variable. You might prefer to access indexes in this fashion whenever your script uses only few images.

Part 1: Creation Part 2: Columns Part 3: Rows and Cells Part 4: Events