All desktop applications have Windows. In a sense, that's what defines a desktop application. Windows all have controls, because without them, your user wouldn't be able to do anything. REALbasic provides a Window class and the next section reviews how to edit and layout Windows in REALbasic and the different kind of Windows that are available to you. Then I highlight the Control classes that I have used in the RSSReader application. There are too many Controls to review all of them, but the ones that I have included are what I consider to be the most important ones, especially EditFields and ListBoxes. Simple WindowsBefore I get too far along in discussing the Window class, I want to review some simplified classes that REALbasic provides that you can use for dialogs and other similar forms of user interaction. This includes the MsgBox function and the MessageDialog classes. The distinguishing characteristic of these windows is that you do not use the Window Editor or Layout Mode to create them. Everything is done for you programmatically. Because they are simplified, you will not use them as the primary Windows in your application by any means. Rather, you will use them to notify the user of important pieces of information, to request information or feedback, or to help the user find a folder or a file. MsgBoxThe MsgBox function is about the easiest Window there is to work with. As of REALbasic 5.5, a new MessageDialog class was added that does everything the MsgBox function does, plus more. Still, there are times when you need to show a quick message to your user or get some feedback, and MsgBox does just what you need it to do. MsgBox isn't a classit is a function, which means that you call it just like every other function. The advantage of having the MsgBox as a function is that functions return values, and in the case of the MsgBox, it returns a value that indicates which button on the MsgBox Window was clicked. More often than not, you'll use a MsgBox to communicate some kind of information to the user. A lot of times, you're letting them know that an error occurred or something like that. MsgBox is an overloaded function, which means that it's really three functions, which are called as follows: MsgBox "An error has occurred." If you want to find out what the user's response was, you can call MsgBox like this: Dim result as Integer result = MsgBox "An error has occurred. Would you like to try again?", 5, "Error" In this example, you can see I've added two more parameters. In addition to the message, I've sent the integer 5 and another string with the value of "Error". The second parameter determines how many buttons the MsgBox has and what they say. The second parameter is the sum of three numbers. The first of the two numbers determines how many buttons there are and what they say. The second of the two numbers determines what kind of icon to show the user. The third number determines which button is the default button. Button Values
Icons
Default Button
Suppose you wanted to show an alert that displays the OK and Cancel buttons, with the OK button being the default button. In addition to the buttons, you want to show the question icon as well. The first thing you do is to add up the three numbers that represent what you want to do. 1 + 32 + 0 = 33 You would then call MsgBox like this: Dim result as Integer result = MsgBox("My message", 33, "aTitle") MsgBox "My Results: " + Str(result) This produces a sequence of two MsgBoxes. The first will look like Figure 5.2 on a Macintosh: Figure 5.2. MsgBox.
The result will be one of eight values, depending on how many buttons the MsgBox has.
After you receive the result, you can proceed accordingly. In this example, if the user pressed the Cancel button, the number 2 is returned in the result. You can see an example of the results in Figure 5.3. Figure 5.3. MsgBox results.
Look at the CancelClose event of the RSS class and you can see the MsgBox function used to ask whether the user really wants to quit before leaving: Dim res as Integer res = MsgBox("Are you sure you want to quit?", 17, "Quit RSSReader") If res = 2 Then Return True End if You may be tempted to use a MsgBox for a little debugging by periodically inserting a MsgBox in your code and calling it to display the value of a certain variable or some other piece of information about the internal workings of your program. That used to be one of the only ways to get that kind of information, but it quickly gets to be a hassle. REALbasic offers a better alternative for accomplishing the same thing, which is to write debugging messages to the console, which is discussed later in this chapter. MsgBoxes look and behave differently on various platforms. On a Macintosh, it has a title bar and no title, and the Window has fixed width and wrapped text. The Windows version has a title and a Close button, which means the user can close the Window by clicking OK or by clicking the Close button. The window will also increase in width depending on how long the longest paragraph is. GetOpenFolderItem, GetSaveFolderItem...SelectFolderREALbasic provides a set of functions that simplify the process of letting a user select a file or folder. For instance, when you want the user to be able to select a file that already exists: REALbasic.GetOpenFolderItem(aFilter as String) as FolderItem The preceding filter will be a reference to the file types. In the RSSReader application, the command looks like this: GetOpenFolderItem("RSSFileTypes.RSS") When you want the user to select a location in which to save a new file: REALbasic.GetSaveFolderItem(aFilter as String, aDefaultName as String) as FolderItem When you want the user to be able to select only folders and no files: REALbasic.SelectFolder() as FolderItem MessageDialogThis class provides functionality similar to the MsgBox in the previous section, but it also gives you a lot more flexibility. When using the MessageDialog class, you will be using the MessageDialogButton class as well. Flexibility is a good thing, but it often can get you into trouble. The trade-off with more flexibility means that you have a greater chance of doing something nonstandard, or presenting the information in a way that is not the usual way it is done on the three platforms. It is always a good idea to be as consistent as possibledon't get carried away with all your new-found powers. (The Macintosh implementation of this class doesn't do a good job of handling Macintosh icons. You could roll your own, but what's unique about both MsgBox and MessageDialog is that they implement themselves as functions, which means they handily return a value when the user clicks the button. Getting the same, or similar, effect on your own is a little more involved.) FolderItemDialog, SelectFolderDialog, OpenDialog, SaveAsDialogNot all of the properties for the three FolderItemDialog subclasses are available for every platform. The following properties are supported on all three platforms for each subclass: FolderItemDialog.Top as Integer FolderItemDialog.Left as Integer FolderItemDialog.Title as Integer FolderItemDialog.InitialDirectory as FolderItem The OpenDialog class provides support for the following properties on the associated platforms: OpenDialog.PromptText as String (Macintosh) OpenDialog.ActionButtonCaption as String (Macintosh) OpenDialog.CancelButtonCaption (Macintosh) OpenDialog.SuggestedFileName as String (Windows) The SaveAsDialog class supports the following: SaveAsDialog.PromptText as String (Macintosh) SaveAsDialog.ActionButtonCaption as String (Macintosh) SaveAsDialog.CancelButtonCaption as String (Macintosh) SaveAsDialog.SuggestedFileName as String (Windows, Macintosh, Linux) SaveAsDialog.Filter as String (Windows, Linux) Finally, the SelectFolderDialog class has these properties: SelectFolderDialog.PromptText as String (Windows, Macintosh) SelectFolderDialog.ActionButtonCaption as String (Windows, Macintosh) SelectFolderDialog.CancelButtonCaption as String (Macintosh) SelectFolderDialog.SuggestedFileName as String (None) SelectFolderDialog.Filter as String (None) Because these are all classes, they need to be instantiated like any other class. After you have instantiated the object, you can then set property values, as shown next. With the properties established, you can call either ShowModal or ShowModalWithin functions that will cause the dialog to be displayed and will return the selected item. ShowModalWithin causes the dialog to be treated like a Sheet on Macintosh computers. For Windows and Linux, it acts as if ShowModal were called. Dim dialog As New SelectFolderDialog Dim Workspace as FolderItem Dim InitialDirectory as FolderItem dialog.ActionButtonCaption = "Select" dialog.PromptText = "Select Workspace" dialog.InitialDirectory = DocumentsFolder Workspace = Dialog.ShowModal() If Workspace <> Nil Then self.EditField1.Text = Workspace.absolutePath Else self.EditField1.Text = "No Workspace Selected" End If The previous example is straightforward enough. The user is asked to select a folder that will serve as the "workspace" for the application, which is where the application's documents will be stored. The path of the selected folder is displayed in an EditField. Editing WindowsWhen you start a project, Window1 will already be in place for you. To edit code or modify the Window, you double-click Window1; the Window1 tab will appear and get positioned at the font of the other tabs, if any others are open. There are two modes: Edit Layout Mode and Edit Mode. These are managed by the far left button on the toolbar. You can also use Option+Tab (Mac) or Alt+Tab (Windows/Linux) to switch between the two views. Layout Mode will look like Figure 5.4: Figure 5.4. Window Panel Layout Mode.The Layout Mode panel is divided into three columns. The left Member ListBox contains a list of available controls. In the center of the Window is a representation of the Window you are editing. This is where you will drag and drop your controls to build your user interface. In the right column is the standard Properties pane that contain the properties and values for whatever item is selected. If you are editing a new Window in a new project, all you will see in the Member ListBox are the events that are available to this Window under the heading event handlers. Even though you are doing it graphically, you are still actually subclassing the Window class. If you are in Edit Mode, you can add new methods, events, properties, and constants, the same as you would when subclassing any other class. Switch to Edit Layout Mode and drag a BevelButton control onto the Window and then switch back to Edit Mode. Now, another item in the Member ListBox has appearedControls. Click and expand this item and you will see BevelButton1 list, which is the name of the Control you just dragged onto the Window. Expand the BevelButton1 row, and you will see a list of events you can implement. When working with Controls, all you need to do is double-click the Control while in Layout Mode and you will automatically be taken to the Control's events in Edit Mode. Unlike Windows, you cannot add methods to Controls that have been dragged onto a Window. You can only implement the events. If you want to add methods to a Control, you need to subclass the control and then place the subclass on the Window. Global Window FunctionsAs you might expect, REALbasic engineers have provided some convenience functions to make working with Windows much easier. Although some applications have only one Window open at a time, there are many applications that will have many Windows open at once. You can use the WindowCount function to find out how many Windows are open. REALbasic.WindowCount as Integer An open Window is one that has been instantiated and loaded into memory. This is important to understand, because you can have open windows that are not visible, which will make a difference in how you proceed. When you call the WindowCount function, you will have returned the number of open Windows, whether they are visible or not. The WindowCount function is often used in tandem with the Window function. You use the Window function when you want to get a reference to a specific window. A REALbasic application treats multiple windows as an array of Windows. The following example loops through a group of Windows and assigns their position as their name. Dim w as Window Dim x,y as Integer // Remember that arrays are "Zero" based y = WindowCount - 1 For x = 0 to y Window(x).Title = Str(x) Next Windows ID PropertiesWhenever a new desktop application product is started, it prepopulates the project with a Window called Window1. The ID Properties for Window1 are the same as they are for any class. You have the opportunity to select a name, specify which interfaces the Window implements, and identify its super class, which, by default, is Window. By default, Window1 is the default Window, which means that it's the one that opens up automatically when the program is launched. You can change this by setting the App.DefaultWindow property in the Property pane of the App tab. Windows can be subclassed, but they have some unique characteristics; for instance, you do not have to instantiate them in many cases. Window1 is implicitly instantiated when you start the application. You can create additional instances of Window1 by doing the following: Dim w as Window1 w = New Window1 w.Show If you subclass additional Windows, the first instance of those Windows can be implicitly instantiated by calling the Show method as follows: Dim w2 as Window2 w2.Show Any additional Windows of this type would need to be instantiated in the normal way with the New operator. One feature of REALbasic is that you can also implicitly instantiate this first Window by referring to any of its properties and setting them. This greatly simplifies the process, but it can also come back to bite you, with Windows appearing before you want them to. When opening multiple Windows of different types, you can declare them this way: Dim w1, w2, w3 as Window w1 = new Window1 w2 = new Window2 w3 = new Window1 The reason this works is that Window1 and Window2 are both subclasses of Window. This is especially helpful if you will not know what kind of Window you will need to instantiate until runtime. In the previous example, I used the Show method to make an instantiated Window visible. There are some variations of the Show method to discuss. These are Window.ShowModal Window.ShowModalWithin(aWindow as Window) The use of the term modal can be confusing. A natural question to ask is, why do I need to use ShowModal when I can set the type of Window as a Modal Window? The answer is that a Modal Window and a Moveable Modal Window keep the user from interacting with any other Window in the application except the Modal Window. Making the Modal Window visible by using ShowModal stops the main thread of your application. ShowModalWithin is used with Sheet and Drawer Windows to identify which Window it should be attached to. Windows Position PropertiesHeight, Width, MinHeight, MinWidth, MaxHeight, MaxWidthThe Min and Max values set the legal range of sizes of your window. Height and Width are the actual values to use when first launching the window. The Min and Max values establish constraints that limit how much the user can resize the Window if it is resizable. These particular values should be used in conjunction with the Screen function to determine the available screen space. REALbasic.ScreenCount as Integer REALbasic.Screen(index as Integer) as Screen The Screen function returns a Screen object. Because a computer can have more than one screen attached, the Screen function is indexed; the default screen is screen 0. ScreenCount will provide a total count of the number of available Screen objects so that you can iterate through them if necessary. Here is an example of getting a reference to the main Screen: Dim aScreen as Screen aScreen = Screen(0) After you have a reference to the Screen object, you can access the following properties that tell you what the available space is for your application Window, taking into consideration the MenuBar (on Macs) and the position of the Dock and Tray. Screen.AvailableHeight as Integer Screen.AvailableLeft as Integer Screen.AvailableTop as Integer Screen.AvailableWidth as Integer Here is an example of how you can use the preferences class to save the size and position of a Window when you exit an application and restore it after you restart it. In the Close event of the App object, do the following: App.Preferences.set("window1.top") = Str(me.Top) App.Preferences.set("window1.left") = Str(me.Left) App.Preferences.set("window1.width") = Str(me.Width) App.Preferences.set("window1.height") = Str(me.Height) In the Open event of the App object, do this: Window1.top = val(App.Preferences.get("window1.top")) #if TargetMacOS then if Window1.top < 36 then Window1.top = 72 end if #end if Window1.left = val(App.Preferences.get("window1.left")) Window1.width = val(App.Preferences.get("window1.width")) Window1.height = val(App.Preferences.get("window1.height")) The final position property in the Layout Editor is Placement. Window1.Placement as Integer You can assign the proper value to the Placement property using the following Window class constants: Window.PlacementDefault = 0 Window.PlacementMainScreen = 2 Window.PlacementParent = 1 Window.PlacementParentScreen = 3 Window.PlacementStagger = 4 The default placement is Stagger, which places the child Window down and a little to the right relative to the parent Window. The PlacementParent property means simply that the new Window is placed in front of the parent, which is the Window from which the new Window was launched. If you have only one screen, the MainScreen and ParentScreen are the same, and they both center the new Window in the center of the screen rather than the parent Window. If the Window is a Drawer, the following class constants apply: Window.PlacementDrawerBottomRight = 2 Window.PlacementDrawerCenter = 1 Window.PlacementDrawerTopLeft = 0 Drawers are Macintosh-only Windows that are attached to the parent Window and slide in and out of the parent Window from the position specified. Windows Appearance PropertiesThe previous Windows were all dialogssimple Windows intended to communicate information to the user or to solicit information from the user. You wouldn't write an entire application using Windows such as these, so REALbasic provides you with a much more flexible and powerful Window class that you can use. Whenever you create a Window, you will select one of the following types for that Window.
Window.Frame as IntegerThis property determines the type of Window that will be displayed. The following class constants refer to the different Window types: Window.FrameTypeDocument Window.FrameTypeDrawer Window.FrameTypeFloating Window.FrameTypeGlobalFloating Window.FrameTypeMetal Window.FrameTypeModal Window.FrameTypeMovableModal Window.FrameTypePlain Window.FrameTypeRounded Window.FrameTypeShadowed Window.FrameTypeSheet Document WindowThis is the default Window type and the one that will more than likely take center stage in any application. The Windows from all three platforms are remarkably similar, with only some minor differences in user interface. By default, Window1 and all new Windows are Document Windows. You can see an example of a Macintosh Window in Figure 5.5. See Figure 5.6 for an example of a Window in Windows, and Figure 5.7 for a Window on the Linux platform. Figure 5.5. Macintosh Document Window.
Figure 5.6. Windows Document Window.
Figure 5.7. Linux Document Window.
Modal Window and Movable Modal WindowA Modal Window is a Window that stays in front of all the other windows of the current application until it is dismissed. The user can move the Window around, but he can't activate a Window behind it. Use this Window when you need information prior to continuing. A Movable Modal Window is one that the user can move around with his or her mouse. A cross-platform issue is that Macintosh Modal Windows do not have a Close button on them. I found this out the hard way. This means that the only way for users to get rid of the Modal Window is if you provide them with a button of some sort that closes the Window. On Windows and Linux, a Close button is standard, but if you think about it, there is a certain amount of logic to the Macintosh approach. If you've bothered to display a Modal Window and blocked access to the other windows of your application, you've probably done so for a reason. You really should never throw up a Modal Window without getting some feedback from the userthat's the point of Modal Windows. When programming on Windows and Linux, be mindful that the user can click the Close button on the Window so that your application will respond appropriately. A Modal Window is like a Movable Modal Window except that it doesn't move. In both cases, the Quit MenuItem is disabled when it is displayed. There are a few differences between platforms, so it is best to describe them by system:
Floating WindowFloating Windows are like Modal Windows in that they always stay on top of the application. However, you can interact with the top-most Window behind a Floating Window.
Floating Windows are useful because they always stay in the activated state, which is why they are used for palettes and toolbars in many applications. This means that every time you access a palette, your main Document Window doesn't deactivate, making you reactivate it by clicking it every time you refer to something in one of your pallets. Global Floating WindowIt's just like a Floating Window, but it stays in front of either all application windows, or of a particular application Window when you set the value of the FloaterProcess property on Macintosh systems. You provide the four-character Mac Creator Code of the application you want the global Window to float in front of. On Windows, it can be used outside of the MDI Window and is not bound by it like other Window types are. Plain BoxA Plain Box Window is just a plain box. It's a Window without a title bar. It's good for splash screens. Macintosh Sheet WindowsOS X only. The Window slides into view from the title bar. The advantage of a Sheet Window is that it acts like a Modal Window, but it is linked to a particular Window in your application and does not block access to other Windows in the application. In other words, if you have an application that allows you to open multiple documents at the same time, you can use a Sheet Window to ask the user for confirmation when saving the document. Although the user can't do anything more with that particular document until he or she responds to the sheet, the user can go to other Document Windows and interact with them. See Figure 5.8 for an example. Figure 5.8. Sheet Window.All other platforms treat them like Movable Modal Dialogs, except Mac OS "Classic," which treats it like a Modal Dialog. Because this kind of Window isn't available on Windows/Linux, those platforms substitute Movable Modal Dialogs instead. Drawer WindowThis is another Mac OS X feature. It's a Window that slides out from behind another Window. In Windows, a Floating Window takes its place and on Linux, a Document Window does. See Figure 5.9 for an example. Figure 5.9. Image of Drawer.Shadowed Box and Rounded WindowThe reason these Window types exist is because they exist on old Macintosh computers. Consequently, I would suggest avoiding them. Window.Composite as BooleanA Composite Window applies to Macintosh computers. Macs have two styles of Windowthe standard kind and Metal Windows. There are some cases when the Window needs to be set to composite for Controls to display propertysometimes the Control isn't transparent and it is surrounded by a rectangle rather than showing through to the underlying color. Metal and Drawer Windows have composite turned on by default. Sometimes problems occur on normal Windows, too, when Controls are stacked on top of each other. If you don't need to use a Composite Window, don'tbecause it seems to perform a little more sluggishly. Window Colors and BackdropsYou can override the default colors and backdrops of Windows very easily in REALbasic. You set the BackColor first by clicking the HasBackColor CheckBox (or setting the HasBackColor property to true). Then click the BackColor row and a Window will pop up that allows you to select the color to use as the background. You can also specify the color in any of the standard ways of expressing a literal color. If you want to get fancy, you can use an image instead. It is easy to add an image to a windowjust drag one to it and drop it in. You can also drag and drop an image into the Project Editor and you'll be able to reference those graphics throughout your application. Generally speaking, it's better to let REALbasic and the underlying operating system decide what color the Window should be so that your application will conform to their user interface guidelines. Just because you can do it, that doesn't mean that you should do it. Of course, there are always exceptions. One common practice in Macintosh applications is to use a backdrop image in folders from which applications are installed. Window.Title as StringThis property gets or sets the Window title, which appears at the top of each Window. Visible as BooleanIndicates whether your Window is visible. You can make a Window invisible with the Hide method and make it visible with Show. To clear a Window from memory altogether, use Close instead of Hide. FullScreen as BooleanExpand the Window to cover the entire screen. This is different from Maximize because it hides the Dock and Tray. MenuBarVisible as BooleanDetermines whether the MenuBar for this Window is visible. On Windows and Linux, this removes the MenuBar from the individual Window to which it is attached. The effect on Macintosh is a little more startling because the MenuBar it removes is the one that is at the top of the screen. FullScreen set to TRue and MenuBarVisible set to False will let your computer operate like a kiosk, with just the user interface supplied by your application available to the users. Whatever you do, don't do this in a normal application, because users like their familiar surroundings and it's a little rude to be mucking about their computer that way. CloseButton as BooleanDetermines whether a Close button exists on the Window. Resizable as BooleanThe Resizable property determines whether the user can resize the Window by dragging it in the lower-right corner. LiveResize as BooleanIf the Window is resizable, you can also set the LiveResize property, which means that the Window view will be updated as you drag it to a larger size. MaximizeButton as BooleanYou can customize your Window so that it does not have a Maximize button by setting this property to False. MacProcID as IntegerYou can use this property to specify specific Macintosh Windowsobviously, this is not a cross-platform feature. MenuBar as MenuBar1You can have more than one MenuBar in your application, and this determines which one is associated with this particular Window. |