|
Programming Tutorial: January Scope This tutorial is intended for the beginning Be developer. Knowledge of C++ is assumed. The following topics will be covered:
Note: This file is available in text format; source code is also available from the Be FTP site. Reading/reviewing The BeBook while reading this tutorial will enhance your learning experience.....
Introduction The January program displays the calendar for the month of January in a window. In addition, the calendar dynamically redraws as the window is resized.
Source File description
Projects The BeOS development environment allows developers to create project files that can be used in conjunction with the IDE, for a graphical presentation of the project and compilation information and commands. For those from the UNIX world, makefiles and command-line compiling are also supported. The January project gives examples of both.
CodeWarrior users should open the "January.proj" file at this
time, while UNIX fans should open up a terminal and
Application Framework The base application class is main() { BTSApplicationWindow *aWindow; BRect aRect; // We can use a standard BApplication object, because // we don't have anything special to add to what it does // by default. BApplication *myApplication; myApplication = new BApplication('JANR'); // set up a rectangle and instantiate a new window aRect.Set(20, 20, 200, 200); aWindow = new BTSApplicationWindow(aRect); // Once everything is setup, start running the application. myApplication->Run(); // Delete the application object before exiting. delete(myApplication); return(0); }
Application Specific Windows Any application that creates windows will have to subclass
the class BTSApplicationWindow : public BWindow { public: BTSApplicationWindow(BRect frame); virtual bool QuitRequested(); }; Only two methods are defined. Overriding the constructor allows us to insert the code that creates the calendar within the window, as follows: BTSApplicationWindow::BTSApplicationWindow(BRect frame) : BWindow(frame, "January", B_TITLED_WINDOW, 0) { BRect aRect = frame; // rect same size as window. BTSMonthView *aView; // Move rect so top left is a (0,0) aRect.OffsetTo(B_ORIGIN); aView = new BTSMonthView(aRect, "MonthView"); // Lock the window before altering contents. Lock(); // add view to window AddChild(aView); // Unlock after done altering window contents. Unlock(); // make window visible Show(); } We create an object of type The other unusual thing is the All of the drawing actually occurs within the bool BTSApplicationWindow::QuitRequested() { // Tell the application to quit! be_app->PostMessage(B_QUIT_REQUESTED); return(TRUE); } When a user closes a window by any means, the Any application that displays a user interface should exit when the last window closes. If this is not done, the application's main menu will disappear, and the user will be unable to quit the program in an acceptable way. If you need this type of functionality, you should create a "dummy" BWindow object and hide it, only deleting it when the program exits. If you have multiple windows, you need to keep track of the window count and exit the program when the count gets to 0. This problem will disappear in a later release of the BeOS. At this point, we have a basic application framework created; these same definitions of main and of the application window could be used for any single-window application. The only thing that would need to be changed is the view that we would be inserting into the window in the window's constructor.
Graphics and dynamically resizing views The graphics work is all done within the BTSMonthView::BTSMonthView(BRect frame, char *name) : BView(frame, name, B_FOLLOW_ALL, B_FULL_UPDATE_ON_RESIZE|B_WILL_DRAW|B_FRAME_EVENTS) { fBackgroundGraphic = new BTSMonthGraphic(this); SetViewColor(B_TRANSPARENT_32_BIT); } All that needs to be done in the constructor is to create
the graphics object representing the calendar, Note the flags passed at the end of the The particular flags used here specify the following:
Dynamic resizing is easy; since we specified void BTSMonthView::FrameResized(float new_width, float new_height) { fBackgroundGraphic->SetSize(BPoint(new_width, new_height)); }
Drawing Finally, drawing! the calendar graphic is represented by a grid. As
the window resizes, the grid spacing changes. Based on the new
grid spacing, the font size and text for the various labels on
the calendar are selected. We can follow all the action starting
with the void BTSMonthGraphic::SetSize(BPoint size) { fSize = size; Invalidate(); } void BTSMonthGraphic::Invalidate() { // Do the resizing magic // Re-calculate the various size things CalculateParameters(); CalculateDayLabels(); // Make sure the view draws itself fImageNeedsUpdate = 1; } OK...without going into too much detail, The Drawing on the BeOS is not done by the application itself, but by
the application server. The app server is a separate process,
one copy of which is always running. (You can see this by opening up
a terminal window and entering the command " To speed this up, line drawing commands can be bundled in arrays
so that the app server can execute them in a more rapid fashion.
This is done by using the fView->BeginLineArray(7); for (counter = 1; counter < 7; counter++) { BPoint startPoint(xStart+(counter*GridCellSize.x)-2,yStart); BPoint endPoint(xStart+(counter*GridCellSize.x)-2,yEnd); fView->AddLine(startPoint, endPoint, blackColor); } fView->EndLineArray(); The code above results in only one large communication with the app
server, instead of 7 small ones. However, calling The separation of the actual drawing from the drawing commands
brings up an important problem; how does the programmer know when
a drawing command has actually executed? In many cases the app
server may not execute drawing commands as soon as they are received.
In order to force the app server to complete all drawing requests
for a given view, the
BTSStringLabel
void BTSStringLabel::Draw(BView* aView) { if (fNeedsCalculation) Recalculate(aView); // Setting font info aView->SetFontName(fFontInfo.name); aView->SetFontSize(fFontInfo.size); aView->SetFontShear(fFontInfo.shear); aView->SetFontRotation(fFontInfo.rotation); aView->SetHighColor(fColor); aView->MovePenTo(fStartPoint); aView->DrawString(fString); } IconWorld IconWorld is used to do basic resource configuration for your app. The IconWorld section of the BeOS User's Guide describes it very well. For mac developers, think of it as ResEdit for the BeOS, although it has far fewer resources. Open January.rsrc using IconWorld. Note under the "App Info" menu that the flag "Single Launch" is set. This means that one copy of the application can be running per executable file, as opposed to "Exclusive Launch", which means one application can be running on the system, period. Of course "Multiple Launch" means the same app can be launched multiple times on the same system. In this case, the selection of "Single Launch" was arbitrary, but for some apps it may be very important. Note the icon's type is 'BAPP', which means it's associated with the application. If this app had documents associated with it, we could create another icon to represent the documents, and give the icon a type that matched the signature of the document files. Close IconWorld.
Building and Running the Project You can refer to the BeOS User's Guide and Metrowerks documentation for complete details on building applications. Below is a short summary of the steps. You now have a basic application framework that you can use as a jumping off point for your own application. No employees of Be or their children were involved in vomiting episodes (projectile or otherwise) during the writing of this tutorial. We hope the same is true for you. nbsp; For UNIX users In the January directory, type " copyres $(RESOURCE_FORK) $@ setfile $@ The first line just appends the resource information to the end of the file. The second line informs the BeOS that the newly created program is an executable file. For CodeWarrior users Choose "Make" from the project menu. Choose "Run" if you want to run it.
|