Many-to-many checkboxes revisited, part 2: The UI code
by Unknown user
I like to start just about any development project with unit tests, but part of this project involves UI code and that isn't something that's usually easy to plug into a unit test.
So instead I'll take a more traditional approach to testing and create a simple hand-coded EXE using the Win32 EXE Quick Start:
Here I've added a window and a few lines of code to get things rolling:
PROGRAM MAP END include('CML_UI_ListCheckbox.inc'),once ListCheckbox CML_UI_ListCheckbox x long CheckboxQueue queue Checked bool CheckedIcon long CheckedText string(30) end WINDOW WINDOW('UITest'),AT(,,205,234),GRAY,FONT('Microsoft Sans Serif',8) LIST,AT(21,12,161,185),USE(?List),VSCROLL,FROM(CheckboxQueue), | FORMAT('14L(2)|M~X~100L(2)|M~Text~') BUTTON('Close'),AT(84,206),USE(?ButtonClose),STD(STD:Close) END CODE loop x = 1 to 100 clear(CheckboxQueue) CheckboxQueue.CheckedIcon = random(1,2) CheckboxQueue.CheckedText = 'String ' & x add(CheckboxQueue) end open(window) ListCheckbox.Initialize(?List) accept end close(window)
Line 4 is an include file pointing to a class I've created using John Hickey's wonderful ClarionLive! Class Creator, or Clive as I like to call him. I use Clive constantly.
For now the CML_UI_ListCheckbox class is just a shell with a stub Initialize method. I also have a queue to hold some data and code to populate that queue with random values.
The first field in the queue will be used to display the check box, which means displaying an icon. And any field that displays an icon needs to have a Long variable immediately following it.
A word on CML classes
I'm going to be releasing the classes from this article series as part of the The ClarionMag Library, so they all follow the CML compiling convention which by default expects to find the classes in CMagLib.DLL.
When I'm doing development, however, I typically don't want to have to recompile that DLL each time I make a class change. Instead I set whatever app I'm using (hand code app or unit test DLL) to compile the ClarionMag Library classes along with the app:
My app compiles and looks like this:
The list box is picking up the first two fields for display instead of the first and third fields (the second being the icon field). Time to write that initialization code.
CML_UI_ListCheckbox.Initialize procedure(long listFEQ) code self.ListFEQ = ListFeq self.ListFEQ{PROPLIST:Icon,1} = 1
I've set up a class property called ListFEQ on the premise that I'm going to need it later in the class operations. The first thing I do is use the property syntax to specify the first field in the listbox as having an icon. That tells the Clarion runtime to skip that field in the list box which leads to a modest improvement in the display. I can now see the text.
But I still don't have icons in my list box. In the original articles I used the browse box template prompts to specify the icons, but that's a bit cumbersome, and in fact just results in code similar to the following being generated. So why not go right to the source? Note also that I've renamed the original on.ico and off.ico to CML_Checked.ico and CML_UnChecked.ico.
self.ListFEQ{PROP:IconList,1} = 'CML_UnChecked.ico' self.ListFEQ{PROP:IconList,2} = 'CML_Checked.ico'
Now the list box displays the icons (randomly set to checked or unchecked) but I'm still seeing the zero value displayed.
I learned how to fix this from Pete Halsted (see http://archive.clarionmag.com/cmag/v1/v1n11checkbox.html). The trick is to use a picture that specifies a space:
self.ListFEQ{PROPLIST:Picture,1} = '@p p'
Now the list box is rounding into shape:
But there's still a potential issue with those icons. I'll either have to ship them with the product or link them into the program. I'd rather do the latter.
One way to do this is to add the icons under Libraries, Objects and Resource files.
If the icons are linked in I need to prefix their names with a tilde character:
self.ListFEQ{PROP:IconList,1} = '~CML_UnChecked.ico' self.ListFEQ{PROP:IconList,2} = '~CML_Checked.ico'
But it's a hassle always adding icons to the project. And what happens if I forget? The app still runs, but since it can't find the icons no checkboxes are displayed:
There is a better way, using a pragma statement:
pragma('link (CML_Checked.ico)') pragma('link (CML_UnChecked.ico)')
Now the icons are automatically linked in; I don't have to worry about adding anything to the project or shipping .ico files with the product.
For your reference, here is the class header:
include('CML_IncludeInAllClassHeaderFiles.inc'),once CML_UI_ListCheckbox Class,Type,Module('CML_UI_ListCheckbox.CLW'),Link('CML_UI_ListCheckbox.CLW',_CML_Classes_LinkMode_),Dll(_CML_Classes_DllMode_) ListFEQ long Initialize procedure(long listFEQ) End
And the class code:
Member Map End Include('CML_UI_ListCheckbox.inc'),Once !include('CML_System_Diagnostics_Logger.inc'),once !dbg CML_System_Diagnostics_Logger CML_UI_ListCheckbox.Initialize procedure(long listFEQ) code self.ListFEQ = ListFeq self.ListFEQ{PROPLIST:Icon,1} = 1 self.ListFEQ{PROP:IconList,1} = '~CML_UnChecked.ico' self.ListFEQ{PROP:IconList,2} = '~CML_Checked.ico' self.ListFEQ{PROPLIST:Picture,1} = '@p p' pragma('link (CML_Checked.ico)') pragma('link (CML_UnChecked.ico)')
Next time: Making the Initialize call more flexible, and turning checkboxes on and off.