User:CBM-c-/VS-List Browsers part 1

From Vectorworks Developer
Revision as of 15:47, 31 December 2020 by CBM-c- (talk | contribs) (expand)
Jump to navigation Jump to search


Creation and columns

For better understanding please use the comprehensive example at Dialog with List Browser. By --_c_ (talk) 03:08, 31 December 2020 (EST) (previously Orso B. Schmid)


Introduction List_Browsers_part_1 List_Browsers_part_2


Create a List Browser

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

CreateLB(dialogID: LONGINT; componentID: LONGINT; 
	widthInCharacters: INTEGER; heightInCharacters: INTEGER);
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:

  • add Icons always only once
  • add Columns most times once
  • add Column Data Items most times only once
  • add/delete/modify Cells repeatedly

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 (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;
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 among 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 VS:AddListBrowserImage.

There is a single routine allowing to load icon resources without pre-loading them in the Icon List first: VS: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 VS: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 VS: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;
imageSpecifier
the path to the image resource (since VW 2104), for example: 'Vectorworks/ResourceBrowser/WallStyle.png'.
I prefer to "borrow" 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.


Example: toggling only three images

{ add three images to a List Browser }
gImgCnt := -1; { init }
gImgCnt := AddListBrowserImage(gD, cLB_Styles, 'Vectorworks/Standard Images/Visible'); { visible: black eye }
gImgCnt := AddListBrowserImage(gD, cLB_Styles, 'Vectorworks/Standard Images/Invisible'); { invisible: cross }
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 "gImgCnt". 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.


Columns

After creating an (optional) List of icons 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 and 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 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.

Settings Modifiers Column set up

Insert columns using VS:InsertLBColumn. Set for each column:

Outside any loop set for the whole List Browser:

GetNumLBColumns( dialogID:LONGINT; componentID:LONGINT):INTEGER;

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

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

Inserts a column ready to be filled with text cells. Call VS:InsertLBColumn for each column to be inserted, use the returned index for further column settings. Defaults upon creation are:

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 VS:SetLBColumnWidth. Don't pass a zero width.
GetLBColumnWidth( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER; 
	VAR width:INTEGER):BOOLEAN;
	
SetLBColumnWidth( dialogID:LONGINT; componentID:LONGINT; 
	fromColumn:INTEGER; toColumn:INTEGER; width:INTEGER):BOOLEAN;
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 VS: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.
GetLBHeaderTextWidth(className: STRING; allowForSortIcon: BOOLEAN): INTEGER ;

(VW14+) returns the column width needed to fit the column title without resizing. There is a parameter "allowForSortIcon". I didn't try this call yet.


Column Titles

Frequently an array of string is 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.

SetLBColumnImage(nDialogID, nComponentID :LONGINT; 
	nColumnIndex, nImageIndex :INTEGER) :BOOLEAN;
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.
GetLBColumnHeaderJust( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER; 
	VAR justification:INTEGER):BOOLEAN;
	
SetLBColumnHeaderJust( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER; 
	justification:INTEGER):BOOLEAN;

Modify the alignment of the column title. Use following constants:

1 = Left
2 = Center
3 = Right
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;

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.
BUG: don't use GetLBColumnHeaderToolTip if you plan to use your script on VW 13. It crashes VW under Mac (fixed by VW 14, built 90067).


Column Lines

Column lines are the gray vertical lines drawn between each column. They are optional. Columns with control type Radio have an extra option for setting the sub-titles column lines.

AreLBColumnLinesEnabled(dialogID: LONGINT; componentID: LONGINT): BOOLEAN;

Checks on/off status of column lines for a List Browser.

EnableLBColumnLines(dialogID: LONGINT; componentID: LONGINT; 
	enableColumnLines:BOOLEAN);

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.

AreLBRadioColumnLinesEnabled(dialogID: LONGINT; componentID: LONGINT; columnIndex: INTEGER): BOOLEAN;

(only for control type 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.

EnableLBRadioColumnLines(dialogID: LONGINT; componentID: LONGINT; columnIndex: INTEGER; 
	enableRadioColumnLines: BOOLEAN);

Default: FALSE. (only for control type Radio) Draws extra gray vertical lines for each sub-column in the chosen Radio column. For this to show you need:

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.

Column Data Items
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 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. Moreover is influenced by the chosen column's Display Type. The only exception is the control Radio, which uses the insertion order of the 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 and 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.

Column Data Items are to be used only on columns with control type Multi State, Single Instance Icon or 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 or 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
it has the peculiar behavior to load all images starting with the first defined in the Icon List, unregarded the icon indexes passed in VS:InsertLBColumnDataItem. It can only display images ignoring text altogether.
Column Data Items and Control Type
Control Type Edit Display Response on click
1 Static <doesn't respond>
2 Radio
  • Image
  • Text and Image
Shows multiple choices with icons, creates a sub-column for each call to Manage the Data Items List VS:InsertLBColumnDataItem, although it displays the 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.
3 Multi State
  • any
At each click displays the next Data Item by index in the list.
4 Single Instance Icon
  • any
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.

5 Static Icon <doesn't respond>
6 Number <doesn't respond>
7 Multiple Icons <doesn't respond>


Manage the Data Items List

InsertLBColumnDataItem( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER; 
	itemString:STRING; imageOn:INTEGER; imageOff:INTEGER; itemData:LONGINT):INTEGER;

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, Single Instance Icon: you can decide to create a Data Item containing only strings setting both "imageOn" and "imageOff" to "-1".
  • Radio: no string support, but the string must be nevertheless defined.
imageOn
is an icon index of the Icon List, the image to be triggered on user's click. Set to "-1" to make image undefined.
  • 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 Icon: on click shows image index 1. Any other selection shows image index 0.
  • Radio: see dedicated chapter.
imageOff
is an icon index of the Icon List, the image to be triggered on deselection. Set to "-1" to make the icon undefined.
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. 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 and 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 columns: show white space
  • Multi State and Single Instance Icon add the images according to the index passed in the parameter "imageOn". Radio columns add them according to the Icon List's index corresponding to the count of calls of the routine InsertLBColumnDataItem. This has been damn tough to find out.

(only 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.

GetLBColumnDataItemInfo(dialogID: LONGINT; componentID: LONGINT; columnIndex: INTEGER; 
	columnDataItemIndex: INTEGER; 
	VAR itemString:STRING; VAR imageOn: INTEGER; VAR imageOff:INTEGER; VAR itemData: LONGINT): BOOLEAN;


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
FindLBColumnDataItem( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER; itemString:STRING; 
	VAR columnDataItemIndex:INTEGER):BOOLEAN;


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".


RemoveLBColumnDataItem( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER; 
	columnDataItemIndex:INTEGER):BOOLEAN;
	
RemoveAllLBColumnDataItems( dialogID:LONGINT; componentID:LONGINT; columnIndex:INTEGER);

Remove one or all items from a Column Data List.

Examples

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
{ will be ignored since the string is missing }
dataItemCnt := InsertLBColumnDataItem(dlog, LB, col, '', imgIndex, -1, 0);
Example: text only toggles
{ text only toggles: use Edit Display "Text only" }
dataItemCnt1 := InsertLBColumnDataItem(dlog, LB, col, 'Orso', -1, -1, 0);
dataItemCnt1 := InsertLBColumnDataItem(dlog, LB, col, 'Atta', -1, -1, 0);
dataItemCnt1 := InsertLBColumnDataItem(dlog, LB, col, 'Ax', -1, -1, 0);
Example: text/image toggles
{ text/image toggles: use Edit Display "Image and text"}
{ gImgCnt is the count of images loaded in the 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);
Example: only string and one image at start: icon 2
{ 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);
Example: targeted index sequence
{ having 10 items defined in the Icon list }
{ sequence 0, 1, 2, 3 on "Radio" }
{ sequence 8, 9, 6, 5 on "Multi State" }
{ 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);
Example: use imageOff on Radio column
{ having 10 items defined in the 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);
Introduction List_Browsers_part_1 List_Browsers_part_2

gImgCnt := AddListBrowserImage(gD, cLB_Styles, 'Vectorworks/Standard Images/Visible'); { visible: black eye } gImgCnt := AddListBrowserImage(gD, cLB_Styles, 'Vectorworks/Standard Images/Invisible'); { invisible: cross }

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 "gImgCnt". 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.


Introduction List_Browsers_part_1 List_Browsers_part_2