GRAphical Programming for Eiffel index contents

Chapter I. First Steps


Introduction

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.


First program

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.


How to implement window

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.


How to draw in window

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.


How to handle mouse click

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.


How to assign menu to your application

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.


How to process menu action

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.


© Object Tools -- info@object-tools.com -- December 1999