Tips_gui   >   Windows   >   Windows

Windows

The window is the where the user and your application intersect, hence an important part for your application, both visually and functionally.

This section includes various tidbits of code and ideas which apply to window classes.

$cfield vs $cobj

$cobj

is the current object where the event is occurring.

$cfield is the current field which the method belongs to.

$cobj can be used in event handling method code.

; $event method of a field.

On evAfter

If len($cobj.$contents)=0
   ; The field is empty.
Else
   
End If

If you want a method to run in the $construct of a field, $cfield is the reference to that field.

; $construct method of a button.

; Set a reference to the button field.
Set reference irOkayButton to $cfield

Group and Scroll Boxes

Do you use group boxes to group your radio buttons? They work great especially as containers for radio buttons. Move the group box around in design mode and you're moving its contents just as you laid them out. Disable it and you've disabled its contents.

Tip

I prefer scroll boxes over group boxes. The top edge of the group box takes up too much space if I'm not using the group box title. If you set the vertical and horizontal scrollbars to kFalse in a scroll box you've got a group box with narrow borders. To make the scrollbox invisible set the $background property to kBGThemeParent and $borderstyle to kBorderNone

Naming Window Objects

Some developers prefix their window object names with 2 lower case letters to indicate the type of object. For example:

There is less need for this naming convention in the more recent versions of Omnis Studio. If you are looking at the method editor of a window class click the View menu in the IDE toolbar, and select Show Icons. Omnis Studio will then display a small icon beside each window object to indicate the type of object. The icons match those in the F3 Component Store.

If you right-click on the window class in design mode and select Field List... Omnis Studio opens a treelist window which lets you to browse the window objects. This treelist also displays a small icon beside each window object to indicate the type of object.

Remove objects

There are several ways to remove objects from a window instance:

Build a list of the objects and then loop through the list.

; Make a list of the objects.
Do $cinst.$objs.$makelist($ref) Returns List

; Loop through the list removing the objects.
For List.$line from 1 to List.$linecount step 1
   Do $cinst.$objs.$remove(List.C1)
End For

Use $sendall to remove all the objects in a single line.

; One line $sendall to remove all the objects.
Do $cinst.$objs.$sendall($cinst.$objs.$remove($cinst.$objs.1.$ref))

Note

The key to this is that $ref always points to the last object referred to. If you were to say: $cinst.$objs.$remove($ref), it would point to the collection $cinst.$objs - in other words trying to remove the collection from itself. But, the first member of the collection can be referenced as $collection.1.$ref. What the above sendall does is, for each object in the collection, removes the first object in the collection (in other words, itself). (The sendall code and explanation was provided by Tim Stewart)

To use $sendall to remove a specific type of object you need to make the objects invisible rather than remove them.

; One line $sendall to hide a specific type of object.
Do $cinst.$bobjs.$sendall($ref.$visible.$assign(kFalse),$ref.$objtype=kOval)

Another option is to build a list of the objects and then issue a $sendall to the list.

; Make a list of the objects.
Do $cinst.$bobjs.$makelist($ref,$ref.$objtype) Returns List

; Issue a $sendall to the list to remove all the kOval objects.
Do List.$sendall($cinst.$bobjs.$remove(List.C1),$ref.C2=kOval)


Resizable Windows

If you make a resizeable window it's nice to have the objects on the window correctly reposition themselves as the window is beign resized.

A technique I use extensively is to set the $edgefloat property of objects to: kEFposnTopToolBar, kEFposnBottomToolbar, kEFposnLeftToolBar, kEFposnRightToolBar, kEFposnHorzHeader, kEFposnMainHeader, kEFposnVertHeader, kEFposnClient

Many times it works best to add a scrollbox object, set its $edgefloat property, and then add objects inside the scrollbox.

For example a set up pushbuttons along the bottom of the window would be placed in a scrollbox which we name BottomContainer. The object's $edgefloat is set to kEFposnBottomToolBar. Push buttons are added to the BottomContainer scrollbox. Now no matter what size the user resizes the window to, the BottomContainer with its group of buttons will hug the bottom of the window.

If you want to have the buttons hug the right bottom edge of the window, put another scrollbox object inside the BottomContainer and name it BottomContainerRight setting its $edgefloat to kEFposnRightToolbar. Size its width correctly and move the buttons into the BottomContainerRight scrollbox. The group of buttons will now always hug the bottom right edge of the window.

Tip

To make life easier in design mode, for any kEFposnClient objects, set the object to kEFnone in the F6 Property Manager, and add a $construct method to the object with a single line of code: Do $cfield.$edgefloat.$assign(kEFposnClient)

Press the Run Demo to see how scrollboxes can be used to keep window objects where you want them.

Revert Field Properties

If you want to revert the properties of objects in a class instance to their original property values, you can do this by looping through the objects in the instance and setting them to match the objects in the class.

; Set a reference to the instance's class.
Set reference rClass to $cinst().$class

; Loop through the instance's objects.
Do $cinst.$objs.$first() Returns rInstObj
While rInstObj
   
   ; Find the matching object in the class.
   Do rClass.$objs.$findident(rInstObj.$ident) Returns rSourceObj
   If rSourceObj
      
      ; Revert the instance property.
      Calculate rInstObj.$PropertyName as rSourceObj.$PropertyName
      
   End If
   Do $cinst.$objs.$next(rInstObj) Returns rInstObj
End While

Warning

This solution won't work for objects that are in the superclass of the class which your are executing the code.

Sendall to objects

The $sendall method is really handy when you want to Do something to all the objects in your window.

The following are a examples of some scenarios and the sample code for doing them.

Change a property of all objects.

; Change the foreground property of all objects.
Do $cinst.$objs.$sendall($ref.$forecolor.$assign(kColor3DFace))

Change a property of selected objects using the search criteria option.

; Disable any objects with a kColor3DFace forecolor.
Do $cinst.$objs.$sendall($ref.$enabled.$assign(kFalse),$ref.$forecolor=kColor3DFace)

Note the extra search criteria after the comma in the $sendall. What follows is the search criteria of which objects are to be sent the $sendall message. You can make the search filter as complex as you like.

Change a property of all objects relative to the object's current property.

; Move all objects right by 50 pixels
Do $cinst.$objs.$sendall($ref.left.$assign($ref().$left+50))

Note the $ref() inside the $assign(). This tells OMSt to evaluate $ref now, then continue evaluating the string.

Change a property of objects inside a single row of a complex grid (requires using grid exceptions).

; Set the forecolor for all the objects on a particular line of a complex grid.
Do irGrid.$objs.$sendall($ref.[iList.$line].$forecolor.$assign(kGray))

Shape Fields

What are they are and why would anyone use them? Shape fields look like background objects but they can receive events and can contain methods.

The field has a $shape appearance property which can be set to: kText, kRect, kRoundRect, kRect3D, kOval, kLine, kBackPicture

You can use a shape field for column headings in a complex grid by setting the $shape property to kText and then putting your sort list code into the $event method of the shape field. Click the Run Demo button in the StudioTips Browser to see an example.

The only problem I have with shape fields is that they look like text fields so I keep double-clicking them when I want to change the text ... oops ... you can only change the text in the Property Manager.

Slow Window Instantiation

One of the first complaints I had when starting development with Omnis Studio was the length of time it took to open windows. In one of my first windows I loaded it with a 5 layer tab pane and about 100 fields. When the window opened I was shocked at how slowly it opened. Complaints to Tech Support didn't change things. The simple fact is that all the great benefits of object-oriented programming cost something when it comes to instantiating windows. You can have multiple instances of any window, you can have superclass inheritance, and you can have $construct methods at every object and class, so just imagine all the work that Studio has to do when you instantiate a new window instance.

The thing I did to speed up window instantiation is to use empty subwindows in page panes and tab panes.

Lets say you have a 5 layer tab pane. When the user opens the window they only need to see the first pane. That might be all the information they need before closing the window. They might click on tab 3 and then close the window. You can speed up window instantiation by putting an empty subwindow field set to kEFposClient on each layer of the tab pane and leaving the $classname empty. In the $event method of the tab pane object you trap the On evTabSelected event and based on pTabNumber you $assign the $classname before the tab is displayed. In the $construct of the window or the tab pane you $assign the $classname for tab one.

On evTabSelected
Switch pTabNumber
   Case 1
      Do irswTab1.$classname.$assign('wWindowClassName1')
   Case 2
      Do irswTab2.$classname.$assign('wWindowClassName2')
   Case 3
      Do irswTab31.$classname.$assign('wWindowClassName3')
End Switch

The other advantage of using this subwindow technique besides speed is that each subwindow is flat, making it a lot simpler to layout, modify, etc. It takes a while to get used to using subwindows, but once you get past that I think you'll enjoy them.

You can also do some data checking before switching to tab 2 or 3. If the user hasn't entered sufficient information, you can discard the event and not even bother setting the $classname.

Note

If you assign the same $classname a second time Omnis Studio is smart enough not to reinstantiate it.

$framehwnd and $hwnd

The $framehwnd and $hwnd properties were always a mystery to me. The F1 Help documentation didn't fully clarify these properties to me, so when I needed to align a kNoFrame window exactly over a field in another window, I created a demo in StudioTips and started testing these properties in an attempt to understand them. Eventually I emailed Tech Support for further clarification.

Here's what the F1 Help says about $framehwnd:

The window identifier of the outermost enclosing window of the object. The properties of $framehwnd are not assignable. Set a reference to object.$framehwnd.$ref, to access its properties.

The documentation on $hwnd is similar:

The window identifier of the main window of the object. The properties of $hwnd are not assignable. Set a reference to object.$hwnd.$ref, to access its properties

The properties of $framehwnd and $hwnd in the F1 Help are as follows:

As far as I know hwnd is an abbreviation for handle on window.

It sounds like $framehwnd and $hwnd only apply to a window instance, but as I discovered these properties can be applied to window objects; complex grids, tab panes, etc.

This became important when I was attempting to align a frameless window precisely over a field inside a complex grid. Using the $width and $height properties of the complex grid object was close, but not close enough. Changing the complex grid border to an embossed border cause a slight offset. I tried using $framehwnd for the complex grid and discovered its properties worked for the grid.

Tech Support confirmed my discovery as follows:

Objects on windows have borders (in this case the complex grid has an embossed border )

The $framehwnd has the following :

$framehwnd.$width should be the same as grid.$width

Well that clarified things better than the F1 Help!

Click the Run Demo button in the StudioTips Browser to try out the $framehwnd and $hwnd properties. If you shift+click any of the buttons you can step through the code.

The oScreenCoordinates object does all the work for you in figuring out where any field object is in relation to the parent window or in relation to the global screen are. You can copy the oScreenCoordinates object to your own application.

A related discovery is that Omnis Studio will not give you the window height including the titlebar. I had to hard code the titlebar height for each operating system in the oScreenCoordinates object.

Another gotcha I ran into was that Omnis Studio will not give you the size of the window toolbar, window menus bar, window status bar, or window scrollbars. The presense of any of these affects the positioning of fields relative to the screen area. You need to adjust the screen position by each of these elements as applicable. You have to figure out these values for each platform. Toolbar sizes vary based on small or larges icons and whether or not the button text is shown. All of this has been done for you in the oScreenCoordinates object.

The oScreenCoordinates object and demo can be downloaded separately from the studiotips.net website.

oScreenCoordinates also includes public methods which return the overall screen width and height for Mac OS X computers with dual monitors.

$redraw

$redraw(bSetContents=kTrue,bRefresh=kFalse)

Method to redraw the contents and/or refresh a field or window.

The $redraw parameters have been a mystery to me for a long time. Hopefully this tip and demo will help to demystify it for others as well.

It took a few hours to build the demo for this tip to test and analyze the $redraw method and draw some conclusions as to what bSetContents and bRefresh actually do.

Every entry field has a $dataname property. You enter the name of the variable which you want the entry field to display. $dataname could be a task variable, class variable, or instance variable. The variable could be a column in a list or row, or a simple variable.

Every entry field has a $contents property. The $contents property is normally equal to the value of the variable being represented by the entry field... but not necessarily.

When you enter the field (evBefore), Omnis Studio refreshes $contents with the variable value.

If you modify the field, $contents will be different than the variable value while your are still in the field.

When you leave the field (evAfter) Omnis Studio copies the $contents to the variable. Omnis Studio does this just before sending the $event(evAfter) message to the field. (That's what makes it difficult to test and figure out when $contents is copied to the variable.)

bSetContents=kTrue will set $contents to match $dataname.
bRefresh=kTrue will refresh the field contents.

If you are changing the variable value in your code, you want bSetContents to be true (default) when you redraw so that the $contents will be set from the variable.

If you are changing the $contents from your code, and you do a $redraw, you want bSetContents to be false so that the $contents value isn't wipes out by the variable value.

Note

Changing $contents only seems to work if the focus in on the field. If the focus is on another field $contents gets replaced by the variable value when you enter the field (evBefore).

I had hoped to find a way to push the $contents to the variable using $redraw but didn't succeed. You can test all of the above with the demo included with this tip.

I didn't figure out a lot of use for setting bRefresh to true vs. false. If you know situations where setting bRefresh to true vs. false is important, please let me know and I can add it to this tip.