In this chapter you can see the basic features of GRAPE - an excellent Eiffel Windows Library for building applications under Graphical User Interface (GUI) shells. If you are experienced in GUI programming, you will have no problems in understanding GRAPE - it is very similar to many other Object Oriented (OO) Libraries. If you are a novice in GUI programming, do not worry, GRAPE was written to simplify building such kind of programs. You can forget about many details while building standard applications. But any default can be replaced by an advanced GRAPE programmer.
Let's look at the simplest program on GRAPE. Following the tradition, it will be the program, writing anywhere "Hello world!".
class FIRST_APPLICATION inherit APPLICATION rename make as app_make end creation make feature make is do app_make ("Hello world!") -- application title text run end end -- class FIRST_APPLICATION
On the example above you can see class FIRST_APPLICATION. All the functionality is inherited from GRAPE's class APPLICATION. Any GRAPE application has such attributes as Status line, Speed bar and Desktop. On the other side the application itself is a window. Thus, as well as any other window it has a title bar, menu bar, system menu and minimize/maximize buttons.
Figure 1.1 GRAPE's simplest application "Hello World!".
This example specifies an application title text as a parameter for application's
creation procedure and then calls feature run. This feature is contained in most
Windows Libraries and performs Message Loop - a standard piece of code: all events
such as a mouse movement or keyboard input are processed by this code by calling the
proper event responder (so called callback).
For example, when User presses the left mouse button - the message loop invokes the
special feature on_lbutton_down from class APPLICATION. By default this
feature does nothing. The basic idea of GRAPE is to allow you to redefine all such
features to customize default object behavior.
More information about callbacks you can find in the next chapter. And now we discuss a more complicated example.
In the example below we examine how to implement window reactions on external events. Remember that this chapter is only a brief tour and the detailed description you will find in the next chapters.
class WINDOW_WITH_TEXT inherit WINDOW rename make as wnd_make redefine -- redefine this callback features on_paint, on_lbutton_down, on_lbutton_up, on_rbutton_down, on_rbutton_up, on_lbutton_double, on_rbutton_double end creation make feature str : STRING text_font : FONT make(s : STRING) is -- this feature create our window object -- here we initialize some attributes before window creation local r : RECT do !!text_font.make_default(text_font.STD_SYSTEM_FONT) !!r.make(0,0,100,100) !!str.adapt(s) wnd_make(s,r) end set_font(the_font : FONT) is -- font of the window text can be changed using this feature require the_font /= Void do text_font := the_font end on_paint: INTEGER is -- paint contents of the window local gc : DISPLAY_GRAPHICS_CONTEXT do !!gc.make_with_assignment(Current) gc.begin_paint gc.select_font(text_font) -- set current font gc.put_string(str, 0.0, 0.0) -- print string gc.end_paint Result := Processed end on_lbutton_down(x : INTEGER; y : INTEGER) : INTEGER is -- when left mouse button was pressed do str := "Left mouse button pressed" repaint Result := Processed end on_lbutton_up(x : INTEGER; y : INTEGER) : INTEGER is -- when left mouse button was released do str := "Left mouse button released" repaint Result := Processed end on_rbutton_down(x : INTEGER; y : INTEGER) : INTEGER is -- when right mouse button was pressed do str := "Right mouse button pressed" repaint Result := Processed end on_rbutton_up(x : INTEGER; y : INTEGER) : INTEGER is -- when right mouse button was released do str := "Right mouse button released" repaint Result := Processed end on_lbutton_double(x : INTEGER; y : INTEGER) : INTEGER is -- when left mouse button was double-clicked do str := "Left mouse button double-clicked" repaint Result := Processed end on_rbutton_double(x : INTEGER; y : INTEGER) : INTEGER is -- when right mouse button was double-clicked do str := "Right mouse button double-clicked" repaint Result := Processed end end -- WINDOW_WITH_PICTURE
In the example above class WINDOW_WITH_TEXT implements a window with some text.
This text depends on the mouse button pressed last.
All redefined callbacks are listed in the feature adaptation section:
class WINDOW_WITH_TEXT inherit WINDOW rename make as wnd_make redefine on paint, on_lbutton_down, on_lbutton_up, on_rbutton_down, on_rbutton_up, on_lbutton_double, on_rbutton_double end creation make
If you want to determine what the particular interface class is doing you should always look at this section first. What is the class WINDOW_WITH_TEXT doing? It behaves as a standard class WINDOW but reacts differently on such events as "painting window", "left mouse button pressed", etc.
Feature on_paint is responsible for window redrawing. When Windows redraws an interface object this callback is executed. At any time the object must know how to process external events including repainting itself in contrast with usual non event-driven programming.
Note that a window title as well as a window border, window background and menu bar (if any) are repainted by Windows automatically. You are responsible only for the window contents.
on_paint : INTEGER is local gc : DISPLAY_GRAPHICS_CONTEXT do !!gc.make_with_assignment(Current) gc.begin_paint gc.select_font(text_font) gc.put_string(str, 0.0, 0.0) gc.end_paint Result := Processed end
Concepts of GUI (Graphical User Interface) drawing are based on term Graphics Context (GC).
It means, that all drawing operations take place on graphics context (special painting
surface similar to a painter's canvas). Graphics Context is the abstract
device-independent surface providing all drawing features. You can obtain GC for Display,
Printer or Memory. During painting you shouldn't worry about a target device.
In our example we create the GC with the make_with_assignment feature which makes the GC for the current window object. There is one rule you should know. All painting operations on any Graphics Context must be embraces with two features calls: begin_paint and end_paint. Don't forget to enclose your drawing with this pair - it's a must.
In the example above we put two features (select_font and put_string) inside that very pair. These features draw a text with the font selected.
All callbacks are functions that report to Windows whether an event has been processed or not. The last operator in the example tells Windows that you have already processed that event and there is no need in default event handling.
There are very simple callbacks. You can handle mouse clicking by your own code by redefining it. Look at the following example:
on_lbutton_down(x : INTEGER; y : INTEGER) : INTEGER is -- left mouse button was pressed at point (x,y) do str := "Left mouse button pressed" repaint -- force window repainting with new str Result := Processed end
If User presses the left mouse button text "Left mouse button pressed" appears on the window.
The first thing you should know is the predefined name of the feature which handles this particular event. These names are very intuitive ones: on_resize, on_move, on_paint, on_destroy, etc.
There is a group of GRAPE classes that represents menus elements. The detailed menus description you can find in the next chapters. And now only a quick tour.
There are two ways to build a menu: to create manually all its items or via Resource Scripts. Now we consider the first way. The second way will be discussed later.
make_menu () : MENU is local file_menu, window_menu : MENU item : MENU_ITEM popup_item : SUB_MENU_ITEM separator : MENU_ENTRY do !!file_menu.make !!item.make("&New", 0, IDM_NEW) file_menu.add(item) !!item.make("&Open", 0, IDM_OPEN) file_menu.add(item) !!item.make("&Close", 0, IDM_CLOSE) file_menu.add(item) !!separator.make_separator; file_menu.add(separator) !!item.make("E&xit", 0, IDM_QUIT) file_menu.add(item) !!window_menu.make !!item.make("&Cascade", 0, IDM_CASCADE) window_menu.add(item) !!item.make("&Tile" , 0, IDM_TILE) window_menu.add(item) !!item.make("Arrange &Icons", 0, IDM_ARRANGE) window_menu.add(item) !!Result.make_root !!popup_menu.make("&File" ,0, file_menu) Result.add(popup_menu) !!popup.menu_make("&Window",0, window_menu) Result.add(popup_menu) end -- make_menu
This text might look big, but it is very easy-to-understand. It makes the application root menu with two popup submenus. Menu is a group of submenus. Submenu is a group of menu items.
Class APPLICATION has special callback make_menu which is called when the
one is created. You should redefine it if you want to assign your own menu as in the
example above.
There is special callback feature on_command responsible for menu actions.
on_command (cmd_code : INTEGER) : INTEGER is local msg_box : MESSAGE_INFO do Result := processed inspect cmd_code when IDM_NEW then -- User selects "New" menu item !!msg_box.make ("New Menu command processed", "FYI") when IDM_OPEN then ... else Result := not_processed end end -- on_command
In the example above when User clicks the New menu item a message box with the text will appear.