GRAphical Programming for Eiffel index contents

Chapter II. Window Classes Hierarchy

TILE
   GROUP
      WINDOW
         DESKTOP
         SIZABLE_WINDOW
            DESKTOP_ELEMENT
            APPLICATION
      DIALOG
   CONTROL

A window in an application written for the Windows operating system is a rectangular area of the screen where the application displays output and receives input from User. A window shares the screen with other windows including those from other applications. Only one window at a time can receive input from User. User can use the mouse, keyboard, or other input device to interact with this window and the application that owns it.

It is important to understand that from a programmer's viewpoint a window is only a rectangular area on the screen. It may even not to have a frame border, title bar or menu. It's only a screen area that can draw itself and process user actions. Some windows may have several child windows on it. Some may have an ability to resize or move itself.

In the first part of this chapter we describe some window classes. Later in the second part we will explain a simple example demonstrating the stuff discussed. For more information on how windows react on different events (creating, painting, etc.) see the next chapters. Here we consider only the classes dealing directly with the windowed conception and such classes as CONTROL and many other window classes.


Brief classes description

In GRAPE we have a pretty window classes hierarchy that corresponds to different kind of window objects.

TILE

The basic class is class TILE. It is a simple window object that has such attributes as position and size.

GROUP

Class GROUP inherits from TILE and acts as the container of tiles. Class GROUP has basic functionality for managing TILE objects. The main idea of this class is propagation of all operations upon the group to its tiles.

WINDOW

The next in our hierarchy is class WINDOW that presents a tile object that can contain a group of tiles. In contrast with GROUP this class represents not only the container of tiles but a tile object itself.


Tips for advanced programmers: It is important to differ a window in terminology of Microsoft® WindowsTM API (in GRAPE this term corresponds to instances of class TILE) and class WINDOW here.


DESKTOP

Class DESKTOP serves as a windows manager of an application.

SIZABLE_WINDOW

Class SIZABLE_WINDOW presents a window that can be resized. It can be minimized or maximized and has resizable thick frame.

APPLICATION

Class APPLICATION serves as the primary interface between a program and User. Every GRAPE application creates at least one window inherited from class application, called the main window that serves as the main window for the application.

DESKTOP_ELEMENT

Class DESKTOP_ELEMENT is the basic class for DESKTOP members (so called children windows) according to the Multiple Document Interface (MDI) in Microsoft® Windows.

DIALOG

Class DIALOG presents a custom dialog box - a window containing one or more controls to retrieve user input. Here is a typical windowed application and tile's objects compounding it.


Figure 2.1 Application's tiles.

Parents and children among the application windows

Each visible element of a GRAPE application, except the application itself, has a parent window.

One of the class TILE attributes is the parent property. It contains a reference to another tile object called the tile's parent. Usually parent object inherits (directly or not) from class GROUP. Generally speaking relations are the following:

       Application
         Desktop
            Desktop Element
               Desktop Element Children
	          Children of Desktop Element Children 
	             ...etc.

This means that an application's tile is the parent (direct) of desktop. Desktop is the parent of all desktop elements and so on.

When you create your own tile object (e.g. by inheriting from DESKTOP_ ELEMENT) you should add it to one of the existing groups (e.g. DESKTOP). GROUP feature add inserts the object to the group's internal container. Also this feature sets the parent attribute of the tile. It points to the current group.

All operations upon a group are being propagated to its members. For example, when a group is repainting all its members are repainting.

There are several restrictions that follow:

As you may see, the application windows hierarchy looks like a tree. It is possible to retrieve the root of this tree - APPLICATION itself at any moment.


Life and visibility cycle of object

There is a difference between an Eiffel object and its visible vis-a-vis (or partner). When you create an object by invoking its creation procedure, the visible partner is not created immediately. The visible vis-a-vis of an Eiffel object is created when you add it to something visible.

For example, when you add a Desktop_Element to the Desktop (which may be considered to be always visible) it also becomes visible.


Note It is not enough to create custom window object. To put it in work, visualize it you should add this object into some group that have APPLICATION object as parent (or parent of parent, and so on).


The common rule is: "When you add a logical sub-tree of windows to the already visible node of the application window's tree, all this sub-tree becomes visible."

When a tile object is going to be destroyed due to the internal logic or user action:

  1. All visible vis-a-vis of its children are destroyed.
  2. The visible vis-a-vis of this tile is destroyed.

Child window position inside the parent window

Window Client Area

All window objects with a caption bar, resizable frame and menus have two window areas: system and client as shown in picture below.


Figure 2.2 Window areas.

Normally when you specify window size you specify a size of system area rectangle. With GRAPE all window output you can produce only inside client area. There is one feature resize_client_area in class WINDOW that allows you to take in mind only client area window size.


Tile coordinates

A tile's size and position are expressed as a bounding rectangle, given in coordinates relative to the screen or the parent window.


Figure 2.3 Tile coordinates.


The coordinates of an APPLICATION window are relative to the upper left corner of the screen; the coordinates of a child tile are relative to the upper left corner of the parent's tile client area. An application specifies a tile's initial size and position when it creates the tile, but it can change the tile's size and position at any time.


GRAPE logical coordinates

GRAPE supports a flexible scheme of positioning child windows inside their parent window. Coordinates expressed as REAL numbers. The REALs you specify as the child's coordinates are not used directly - they are interpreted by GRAPE.

Here are the rules of this interpretation. You specify the 4 parameters: x, y, width and height of the child window.

 

< 0

0 .. 1

> 1

X

parent.w - abs(x)

parent.w * x

x

Y

parent.h - abs(y)

parent.w * y

y

W

parent.w - abs(w)

parent.w * w

w

H

parent.h - abs(h)

parent.w * z

h

X and Y are calculated from the upper left corner of the parent client area,
X - horizontally; Y - vertically;

Thus, if you want to occupy the right half of the parent's window client area you should specify its coordinates in the following way:

x = 0.5 y = 0 w = 0.5 h = 1.

The child window will follow the parent window resizing.

For example, the following formulas are used for a SpeedBar and StatusLine

SpeedBar : x = 0, y = 0, w = 1, h = 40
StatusLine: x = 0, y = -20, w = 1, h = 20


Note This does not refer to positioning desktop elements inside the desktop.



TILE family

In this section we discuss some meaningful details dealt with a tile's creation, initialization, and basic attributes of each the TILE descendant. There are a lot of useful features that are not presented here, but some of them considered later in this manual. As you may suppose the full list of the class properties is explained in GRAPE's On-Line Help Reference.


TILE

As you may suppose all attributes available in class TILE are available for all GRAPE windowed classes. This is the base of the GRAPE class hierarchy. It contains the basic properties of an object that may be visible and responds to user actions.

name : STRING

Each tile object has its name. For some classes such as TILE attribute name is only an internal property that identifies tile object uniquely. For some tile's descendants attribute name is the caption string of its title bar. Name of tile is specified by the first parameter of tile creation procedure and can be changed during tiles life by the set_name feature.

parent : GROUP

Contains the parent window object of this TILE.

rect : RECT

Contains the logical coordinates of TILE inside the parent window. There is one feature logical_to_physical that converts logical coordinate to physical according to the GRAPE conversion scheme.

prect : RECT

Contains physical coordinates of TILE's client area inside it's parent's window client area. The correct values appear only after first on_move and on_size callbacks, i.e. before first on_paint callback. There are two features screen_to_client and client_to_screen that convert screen coordinates into physical coordinates inside object's client area and back. You can change tile size and position using the resize and move features.

on_parent_resize ( parent_w, parent_h : INTEGER )

Due to the GRAPE logical coordinate scheme you can set tile size depended from size of the tile's parent. This feature adjusts tile size when parent has been resized.

context : INTEGER

Contains the help context of the APPLICATION when the current object is active. See the chapter dedicated to the application help.

makeTile ( tile_name : STRING; r : RECT; ctx : INTEGER)

Creates a TILE object. The tile_name parameter contains name of tile object,
r determines logical tile's coordinates and ctx - tile's help context.

not_processed : INTEGER is 0;
processed : INTEGER is 1;

These two constants play important role in GRAPE tile features. They are discussed in the following chapter.

activate()

This feature activates the tile object. The tile being activated is received from the user input (keyboard keys and mouse events). Only one tile can be active at one moment. Thus, when the tile has been activated the system deactivate-s the previous active tile object by calling its deactivate feature. You can check this state by calling the is_active function.

hide()

This feature hides the tile object. After the tile has been hidden it disappears from the screen. To return it back to the visible state you should call the show feature. When a tile is hidden it cannot be active. You can check the tile visibility by calling the is_visible function.

disable()

This feature disable-s the tile object. After the tile has been disabled it is still visible on the screen (if it was shown before) but cannot be active. It looks as if the tile object ignores the user action. To revert the tile to the normal state you should call the enable feature. You can check this state by calling the is_enabled function.


Note After a tile has been disabled it can still receive messages from the system.


set_interface_style ( style_code : INTEGER, state : BOOLEAN )
get_interface_style ( style_code : INTEGER ) : BOOLEAN

Sometimes you do not need to redefine tile's initialization features. You can set  the particular interface style and achieve the same result. There are some style codes available in class TILE (See also class WINDOW interface styles).


Style code

Meaning

STYLE_DISABLED This predefined style code means that the newly created window object will be initially disabled.
STYLE_VISIBLE This predefined style code means that the newly created window object will be initially visible (default style).
STYLE_GROUP This predefined style code controls the keyboard interface of object when used in dialogs.
STYLE_TABSTOP This predefined style code controls the keyboard interface of object when used in dialogs. If the style is set then the object may be selected in a dialog by using Tab key.

For example, feature enable might look something like this:

enable() is
   do
      set_interface_style ( STYLE_DISABLED, False );
   end;

The next two features determine the tile border style.

border_style : INTEGER
set_border_style ( border_style_code : INTEGER )

A tile object can have a border. This feature sets one of the predefined border styles.

Border code

Meaning

NO_FRAME Tile will not have any border.
SIMPLE_FRAME Tile will have a thin non-resizable border.
RESIZABLE_FRAME Tile will have a resizable border.
MODAL_FRAME Tile will have a modal border provided by windowing platform.
destroy()

This feature destroys the tile object. The window is closed and removed from the parent's group. All future reference to object may be fatal.

can_destroy()

When the application executes the destroy feature it calls this very function. This is "Are_you_sure_to_exit?" conformation for the tile object. If the tile object does not want to be closed it must return constant not_processed otherwise it returns processed. For example you can redefine this feature to get exit confirmation from User.

can_destroy : INTEGER is
   local msg_box : MESSAGE_BOX;
   do
      !!msg_box.make ("Are you sure to close tile?",
                      "Exit", msg_box.YES_NO, msg_box.ICON_QUESTION);
      if execute( msg_box ) = msg_box.YES_BUTTON then
         Result := not_processed;
      else
         Result := processed;
      end;
   end -- on_destroy

Feature above shows the exit conformation message box to User.

init()

This is a special feature that establishes a link from the GRAPE tile object to a physical window object (vis-a-vis) in your operating system.


Note This feature is called automatically from the GRAPE kernel and must not be called by User. You can only redefine it with special care.


Tips for Advanced programmers: There is one tile's attribute sys_id that contains the window handler (HWND) for the current tile object after feature init() has been called.


All window operations - painting, timer callbacks, mouse movements are valid only after init() has been completed. There is special function is_valid that returns True if it happens.


GROUP

As was noted above class GROUP  is used as a container for its child tiles object. It propagates all operation upon the group to its children.

make ()

Creates a GROUP object. Performs some initializations.


Note The creation procedure of class GROUP does not call the creation procedure of its ancestor - class TILE. Thus, the object of class GROUP is not a tile. GROUP redefines most of TILE's features to support its basic idea - the propagation of events. See class WINDOW.


childs : ARRAY [ TILE ]

This array contains the group of children. Function number_of_childs() contains the total number of children in the group. One of the children may be the current one - the special state of a child when it is active - it receives User input. Attribute cchild contains the index of the current tile in the group. Although array childs is exported to {ANY} we strongly recommend to use function child ( I : INTEGER) to obtain the particular group member.

add ( t : TILE ), insert ( t : TILE; before_tile : TILE )

Appends the specified child to the end of the group. Appended child becomes the current in the group. Attribute parent of the appended child is set to point to this GROUP object. After tile has been inserted it can be removed by procedure remove.

can_destroy() : INTEGER

When someone wants to close and destroy a GROUP object the feature "asks" every group's child "what they are thinking about their death". If one of them against - the group returns not_processed - otherwise processed.

Other features which were redefined from class TILE: enable, disable, etc. have the same "broadcasting" effect.

init ()

This is the feature which is redefined from class TILE and does the following things. For every child t of the group the feature performs the following actions:

t.init			   -- initializes the child
t.on_create		   -- forces the creation callback
t.show			   -- sets the visible state
t.repaint		   -- forces repainting
t.on_parent_resize	   -- forces repositioning inside group

Thus, a group performs all actions to wake a child tile object up.


WINDOW

As was noted above class WINDOW unites the group functionality and the tile behavior. In addition WINDOW has a menu and title bar.

make ( tile_name : STRING; r :RECT)

Creates a WINDOW object. Creates an empty group of children, creates a tile object with the specified name and position. Normally tile_name appears on the window title bar.

Class WINDOW brings some new interface styles in addition to ones defined in TILE.


Style code

Meaning

STYLE_CAPTION This predefined style value means that window has a caption in its system area.
STYLE_SYSTEM_MENU This predefined style value means that window has a system menu button in its non-client area.
STYLE_MAXIMIZE_BUTTON This predefined style value means that window has a maximize button in its non-client area.
STYLE_MINIMIZE_BUTTON This predefined style value means that window has a minimize button in its non-client area.
add ( child : TILE )

If window is already initialized and attribute auto_create_child is True then this feature in addition to adding children into the group also initializes the tile just inserted. So it produces all the following initializations:

child.init
child.on_create
child.show
child.repaint
child.on_parent_resize

menu : MENU, set_menu (m : MENU)

You can attach your own menu object to the window. See the chapter about menus.



DESKTOP

As was noted above class DESKTOP operates with its child windows - the Desktop elements. It brings the MDI (Multiple Document Interface) manager functionality to class WINDOW - it can arrange, cascade, tile the children, etc.

make ()

Creates a DESKTOP object. The size of desktop is determined by the size of it's parent - class APPLICATION.

add ( child : DESKTOP_ELEMENT )

In addition to the window functionality this feature activates inserted child. Child must conform to class DESKTOP_ ELEMENT.



SIZABLE_WINDOW

Class SIZABLE_WINDOW adds an ability to minimize, maximize, resize a window object. This window object has its own icon displayed while being minimized.



DESKTOP_ELEMENT

Based on class SIZABLE_WINDOW this class has some low-level differences in its behavior compared with one of the ancestors. For example an object of this class cannot have user menu. To be more precise - the menu of current desktop element is attached to the APPLICATION menu. The default desktop element has the system menu, minimize and maximize buttons and resizable frame.


APPLICATION

make ( app_name : STRING ), make_at_pos ( app_name : STRING, pos : RECT )

Create an APPLICATION object.

make_speedbar : SPEEDBAR
make_statusline : STATUS_LINE
make_desktop : DESKTOP
make_help : HELP
make_menu : MENU

Creates a standard APPLICATION attributes SPEEDBAR, STATUS_LINE, DESKTOP, MENU, HELP.

run

You always must call it to start the application object just created. This feature is very important and that's why we would like to describe it here more precisely. It performs exactly the following job (the real code is so brief and expressive):

  1. The creation of the standard application components:
    desktop    := make_desktop()		add(desktop)
    statusline := make_statusline()		add(statusline)
    speedbar   := make_speedbar() 		add(speedbar)
    
    
  2. After the following initialization the main window becomes visible:
    init()
    
    
  3. Assigning an icon for the application (if any):
    set_icon(icon)
    refresh_background()
    
    
  4. The creation of a user defined menu:
    user_menu := make_menu
    if user_menu /= Void then
     user_menu.set_parent(current)
     set_menu (user_menu)
    end
    
    
  5. The initialization of the children of the main application window:
    if desktop /= Void then
       desktop.init()
       tmp := desktop.on_create()
    end
    --
    if statusline /= Void then
    statusline.init()
       i := statusline.on_create()
          statusline.show()
          statusline.repaint()
          statusline.on_parent_resize(prect.w,
          prect.h)  end
    -- 
    if speedbar /= Void then 
       speedbar.init() i :=
       speedbar.on_create()
       speedbar.show() 
       speedbar.repaint()
    end
    
    
  6. Resizing the components properly:
    coord_setup() -- taking into account
                  -- presents of all components
    
    
  7. Creating the help interface (if any):
    help := make_help()
    
    
  8. Activating all the application components:
    on_create()
    
    
  9. Waiting for an event:
    application_run() -- Run-time library Windows
                      -- message loop
    

TILE Example

Now it's time to include some examples.

Here is an example of the simplest application. Class SIMPLE_ APPLICATION is the Root class and feature make is its creation procedure.

class SIMPLE_APPLICATION
   inherit 
     APPLICATION 
       rename make as app_make end
   creation
     make
   feature
     make is 
     do
       app_make ("Simplest application")
       run
     end
end -- class SIMPLE_APPLICATION

You can find its code in the EXAMPLES\SIMPLEST directory. This program creates the following simple application window.


Figure 2.4 The simplest application.

Most applications also create many other windows - directly or indirectly - to perform tasks related to the main window. Each window plays a part in displaying output and receiving input from User.

The following example is more complicated one. You can find its code in the EXAMPLES\WINDOWS directory.


Figure 2.5 A simple MDI application.

There is class WINDOWS_APP which is the GRAPE application that has its own menu with several items, feature on_command processing the menu commands and an exit confirmation dialog box.

-- 
-- This application demonstrates the basic window classes hierarchy.
--
class WINDOWS_APP
   inherit
     APPLICATION
       rename 
         make as app_make;
       redefine
         on_command, make_menu, can_destroy
       select 
         on_command, make_menu, can_destroy, init
       end

   creation 
     make

   feature
     ----------------------------------------------------------------
     make is
       do 
         app_make ( "Windows application" ); -- name of the
 application
         run 
       end
     ---------------------------------------------------------------- 
     make_menu () : MENU is -- See chapter on menus
       local
         menus : MENU;
         popup : SUB_MENU_ITEM;
         entry : MENU_ENTRY; 
         item : MENU_ITEM
       do
         !!Result.make_root;
         !!menus.make;

         !!item.make("&New child window", 0, IDM_NEW );
         menus.add(item);
         !!entry.make_separator;
         menus.add(entry);
         !!item.make("&Close", 0, IDM_CLOSE );
         menus.add(item);
         !!item.make("Close Al&l childs", 0, IDM_CLOSE_ALL);
         menus.add(item);
         !!item.make("E&xit", 0, IDM_QUIT );
         menus.add(item);

         !!popup.make("&File", 0, menus );
         Result.add(popup);
    
         !!menus.make;
         !!item.make("&Cascade", 0, IDM_CASCADE );
         menus.add(item);
         !!item.make("&Tile", 0, IDM_TILE );
         menus.add(item);
         !!item.make("Arrange &Icons", 0, IDM_ARRANGE );
         menus.add(item);

         !!popup.make("&Window", 0, menus );
         Result.add(popup);
       end;
     ----------------------------------------------------------------
     -- This feature executes every time when User selects some item
     -- from the application menu.
     on_command ( cmd : INTEGER ) : INTEGER is --
       local
         desktop_el : CHILD_WINDOW ;
       do 
         Result := processed;

         inspect cmd 
           when IDM_NEW then -- when User selects "New" from menu...
             !!desktop_el.make ( "Child" );
             desktop.add ( desktop_el );
           when IDM_TILE then
             desktop.tile();
           when IDM_CASCADE then
             desktop.cascade();
           when IDM_ARRANGE then
             desktop.arrange();
           when IDM_CLOSE_ALL then
             desktop.wipe();
           when IDM_CLOSE then 
             if desktop.get_current /= VOID then
               desktop.get_current.destroy();
             end; -- if 
           when IDM_QUIT then 
             terminate();
           else Result := not_processed;
         end; -- inspect 
       end; -- on_command
     ----------------------------------------------------------------
     can_destroy : INTEGER is
         local msg_box : MESSAGE_BOX;
       do
         !!msg_box.make ("Are you sure to exit?",
                   "Exit", msg_box.YES_NO, msg_box.ICON_QUESTION);
         if execute( msg_box ) = msg_box.YES_BUTTON then
           Result := not_processed;
         else
           Result := processed; -- do not exit 
         end 
       end -- on_destroy
   ----------------------------------------------------------------
   feature -- menu commands
     IDM_NEW, IDM_CLOSE, IDM_CLOSE_ALL, IDM_QUIT,
     IDM_CASCADE, IDM_TILE, IDM_ARRANGE : INTEGER is unique;
   end -- class WINDOWS_APP

When User selects the New command from the File menu a new desktop element of type EDIT_WINDOW is being created. Desktop elements can be arranged or closed.

The following is the class EDIT_WINDOW. It is the heir of DESKTOP_ELEMENT. It contains the child window of type MEMO_EDIT. It looks like a simple text editor window.

class EDIT_WINDOW 
   inherit
     DESKTOP_ELEMENT
       rename make as de_make end

   creation
     make

   feature
     edit_text : MEMO_ENTRY ;

     make ( str : STRING ) is
       local r : RECT; -- rectangle
     do 
       de_make ( str );                   -- create itself
       !!r.make ( 0, 0, 1, 1 );           -- entire parent window 
       !!edit_text.make ( "Memo", r, 0 ); -- create edit control 
       add ( edit_text);                  -- insert it
     end
   end -- class EDIT_WINDOW

When object EDIT_WINDOW is being created it creates the edit_text control and inserts it into the children group. When EDIT_WINDOW is added into Desktop it becomes visible with its child's edit_text.


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