4. Ogre: Object Oriented GUI Toolkit |
4.0. Introduction to Ogre |
It is rather easy to build simple graphic interfaces using these primitives. For instance, the function snpaint implements a simple drawing program for drawing lines and rectangles using low level primitives.
Graphic interfaces however become quickly complex. The class browser described in the last chapter of this document contains various features, like menus, buttons, requesters, selectors and scrollbars. A direct program would be overwhelmingly complex.
This is why people have developped various toolkits which handle automatically a large part of this complexity. The Ogre library is such a toolkit entirely written using the Lush object oriented language. This manual describes how to build programs with this library.
In the Ogre library, each component of a graphics interface, a button for example, is implemented as an object which is an instance of a subclass of class VisualObject .
The class of such an object defines both the appeareance of the object and its response to user input. Defining a new type of object (e.g. a menu) is thus just a matter of defining a new subclass of VisualObject .
There is a wide variety of such object classes:
There are two programming levels with the Ogre library. At the simplest level, you just use the provided classes for defining your interface. At the second level, you define subclasses of the standard Ogre classes in order to create new kind of graphic components.
4.1. Ogre Tutorial |
? (ogre) [ogre.lsh] (autoload) = idle-hook
Let us then open a window ww containing a message string and a button arranged in a column named cc :
? (setq ww (new windowobject 0 0 400 300 "Essai" (setq cc (new column (new string "Press button to beep") (new stdbutton "Beeper" (lambda(c) (beep))) ) ) ) ) = ::windowobject:e3100
When you click on the button, the callback function (lambda(c) (beep)) is called and produces an audible beep. Let us add now a check box into the column:
? (==> cc insert (new checkbox "check me" (lambda(c) (printf "Check box state is %l\n" (==> c getdata)))))) = ()
The callback function takes one argument. This argument is the check box object that we can query using method getdata . Let us add now an editable string named ee into the column:
? (==> cc insert (setq ee (new editstring 18 "hello"))) ;; Argument 18 is the field width. = () ? (==> ee setdata "hello people") = () ? (==> ee getdata) = "hello people"
Again we can manipulate the state of the editable string using method setdata and getdata . You may have noticed that the button width has changed when we have inserted the editable string. The column indeed manage its contents in order to keep them properly aligned. We can indeed move and resize the column as a whole:
? (==> cc move 50 70) = (50 20 129 84) ? (==> cc resize 250 120) = (50 20 250 100)
We can even add into the column a small object which lets you move the column with the mouse:
? (==> cc insert (new dragarea 50 20)) = ()
Arguments 50 and 20 are the width and height of this object. Since the drag area is inserted into a column, its width is adjusted to the column width.
Let us add a row with two exclusive buttons:
? (==> cc insert (new row (new radiobutton "choice1") (new radiobutton "choice2") ) ) = ()
We can get (or set) the complete state of the column with a single message getdata (or setdata ):
? (==> cc getdata) = (() "hello people" () t) ? (==> cc setdata '(t "goodbye" t ())) = ()
The Ogre library provides many more complex object, as shown in the following example:
? (setq h (new filerequester ww)) = ::filerequester:f3ef0 ? (==> h popup) = ::filerequester:f3ef0 ? (==> h getdata) = "/home/leon"
Let us add now a button for destoying the window.
? (==> ww insert (new stdbutton "Bye Bye." (lambda(c) (==> thiswindowobject delete)) ) ) = ()
This button ends the tutorial.
4.1.0. Calling Ogre in a Lush Script |
Here is an example of a very Lush script that opens up an Ogre window and runs until that window is closed by the user:
#!/bin/sh exec lush "$0" "$@" !# ;; A simple Ogre GUI demo (ogre) (wait (new autowindowobject 10 10 100 100 "Simple Lush GUI Demo" (new column (new stdbutton " hit me " (lambda (c) (printf "OUCH\n"))) (new stdbutton " feed me " (lambda (c) (printf "CRUNCH\n"))))))The wait function takes one argument. It causes Lush to sit around and keep processing events until the argument is nil. When the argument becomes nil (presumably as the result of processing an event), wait returns.
In the above example, the argument to wait is the WindowObject (i.e. the Ogre window) in which the buttons reside. When the user closes the window, this object is destroyed, hence wait returns, terminating the script.
Using wait in a script that runs an Ogre application is a necessity because scripts terminate as soon as the evaluation of the content of the script terminates. Since the constructors of Ogre applications return immediately (to allow for Ogre apps to run simultaneously with the Lush main prompt), the script woould open the Ogre window and terminate immediately if we did not use wait .
4.2. The Ogre Class Library |
The kernel of the Ogre library ensures these interaction by defining how messages are propagated between the objects in a graphic interface.
4.2.0. Ogre Methods |
Repainting is managed asynchronously. When you want to redraw an object, you must send a message expose to this object to tell the Ogre system to schedule a repainting operation. When the system becomes idle, the Ogre library sends a repaint message to all objects to repaint with a proper ordering which ensures that the topmost objects are repainted last.
Similarly, you never change the location of an object directly. You send message move , resize or moveresize which tell the Ogre system to relocate an object. The Ogre library then enforces all the rules of the container objects, performs a global recomputation of the location of all objects and places the objects by sending them a message geometry .
In general, most operation on the Ogre objects are implemented using two messages.
When you want to perform a certain operation, you must call the request message and not the implementation message. The Ogre library will call the implementation message for you at a proper time and with the proper order.
When you define a new class of Ogre objects, you must define the implementation method only. The corresponding request message will be inherited from the superclass.
Here is a list of the most useful message pairs:
+-------------------+------------------+------------------+ | Operation | Request | Implementation | +-------------------+------------------+------------------+ | Drawing | expose | repaint | | | | repaint-bw | | | | repaint-color | | | | backpaint | | | | backpaint-bw | | | | backpaint-color | +-------------------+------------------+------------------+ | Moving | move | geometry | | and resizing | moverel | manage-geometry | | | resize | compute-geometry | | | moveresize | | +-------------------+------------------+------------------+ | Making an | insert | realize | | object visible | remove | | +-------------------+------------------+------------------+
4.2.1. Ogre Class Hierarchy |
Here is a display of the current class hierarchy in the Ogre library. Classes displayed with a star are the abstract classes.
visualobject * control * editstring editnumber editsecretstring button * stdbutton tinybutton checkbox radiobutton filereqbutton menuitem knob * dragarea sizearea slider * hslider vslider scrollbar * hscrollbar vscrollbar textpane container * form * windowobject * autowindowobject * requester warningrequester errorrequester yesnorequester filerequester printrequester viewer selector edittext column menupopup row grid frame framesize viewerhole emptyspace darkspace string icon menu choicemenu
Here is the use of the main abstract classes:
4.3. Ogre Utility Functions |
4.3.0. Initializing the Ogre library |
4.3.0.0. (ogre) |
[DE] (ogre.lsh) |
This function initializes the library and sets up the event dispatcher. In particular, it creates the object ogre-task and defines the functions event-hook and idle-hook . If the library was already initialized, function Ogre returns immediatly.
4.3.0.1. ogre-task |
[VAR] (ogre.lsh) |
When Lush is waiting for user commands on the standard input, the Ogre library manages events asynchronously. The user can thus either type lisp commands or activate graphic interfaces.
When a Lush program is running, however, the Ogre library manages events when the function process-pending-events is called. It is a good practice to call this function a few times during long programs.
4.3.0.2. (ogre-close-all) |
[DE] |
4.3.1. Error Handling in the Ogre Library |
An error condition occurs when these functions are incorrectly designed. The error message is printed as usual, but you are not prompted for a debug toplevel.
An interruption occurs if you type Ctrl-C in the Lush console. The break message is printed as usual, but you are not prompted for a break toplevel.
If you wish to be prompted for these debugging utilities, you must redefine functions ogre-debug-hook and ogre-break-hook . Here is a simple way to achieve this:
(setq ogre-debug-hook nice-debug-hook) (setq ogre-break-hook nice-break-hook)
4.3.2. Ogre Color Palette |
Ogre provides a way to test if you are using a black and white or a color screen. It also provides several utility functions for displaying three dimensional looking objects.
4.3.2.0. color-palette |
[VAR] (ogre.lsh) |
If you are using a color display, this variable contains an array containing the color numbers used by Ogre for displaying an object. Several variables are used to name these colors:
palette-left (for rendering light) palette-right (for rendering shadow) palette-up (for rendering raised objects) palette-down (for rendering depressed objects) palette-disabled (for rendering disabled texts) palette-selected (for rendering selected objects)
4.3.2.1. (new-palette r g b) |
[DE] (ogre.lsh) |
You can then store the resulting array into variable color-palette for redefining the default palette. You can also pass the new palette to an existing window object using message palette .
4.3.2.2. (getcolor colname) |
[DE] (ogre.lsh) |
This function is equivalent to:
(color-palette <colname>)
4.3.2.3. (setcolor colname) |
[DE] (ogre.lsh) |
This function is equivalent to:
(color (getcolor <colname>))
4.3.2.4. (fill-background x y w h) |
[DE] (ogre.lsh) |
4.3.2.5. (draw-up-rect x y w h) |
[DE] (ogre.lsh) |
4.3.2.6. (draw-up-round-rect x y w h) |
[DE] (ogre.lsh) |
4.3.2.7. (fill-up-rect x y w h [c]) |
[DE] (ogre.lsh) |
The default value for c is the color number indicated by palette-up .
4.3.2.8. (fill-down-rect x y w h [c]) |
[DE] (ogre.lsh) |
The default value for c is the color number indicated by palette-down .
4.3.2.9. (fill-up-round-rect x y w h [c]) |
[DE] (ogre.lsh) |
The default value for c is the color number indicated by palette-up .
4.3.2.10. (fill-down-round-rect x y w h [c]) |
[DE] (ogre.lsh) |
The default value for c is the color number indicated by palette-down .
4.3.2.11. (fill-up-circle x y r [c]) |
[DE] (ogre.lsh) |
The default value for c is the color number indicated by palette-up .
4.3.2.12. (fill-down-circle x y r [c]) |
[DE] (ogre.lsh) |
The default value for c is the color number indicated by palette-down .
4.3.3. Ogre Fonts |
4.3.3.0. (ogre-font size [serifp [monospacep [boldp [italicp]]]]) |
[DE] |
4.3.3.1. (font-18) |
[DE] (ogre.lsh) |
4.3.3.2. (font-12b) |
[DE] (ogre.lsh) |
4.3.3.3. (font-12) |
[DE] (ogre.lsh) |
4.3.3.4. (font-8f) |
[DE] (ogre.lsh) |
4.3.3.5. (font-8) |
[DE] (ogre.lsh) |
4.4. Visual Objects |
Class VisualObject however defines most request messages which are inherited by the graphic components. It also defines the default implementation of most implementation messages.
Four slots are defined by class VisualObject :
(defclass visualobject object (rect (0 0 0 0)) oldrect itscontainer window )
4.4.0. (new VisualObject w h) |
[CLASS] (ogre.lsh) |
This constructor is seldom used for creating new instances. Derived classes (e.g. Control ) usually call this constructor method within their own constructor method in order to initialize the VisualObject part of their instances.
4.4.1. VisualObject Request Messages |
Unless you have a thorough knowledge of the internals of Ogre, you should neither redefine nor override these messages when defining a subclass of VisualObject .
4.4.1.0. (==> VisualObject expose [rect]) |
[MSG] (ogre.lsh) |
Exposure messages are propagated down until they reaches the window object. The clipped rectangle are then added to the damaged area list. Repainting is usually performed when the event queue becomes empty.
4.4.1.1. (==> VisualObject repair-damaged) |
[MSG] (ogre.lsh) |
4.4.1.2. (==> VisualObject moveresize x y w h) |
[MSG] (ogre.lsh) |
This request is then signaled to the object's container which gets a chance to redefine its own geometry and to enforce a particular layout. When these recomputations are finished, the Ogre library effectively calls method geometry to relocate the objects.
4.4.1.3. (==> VisualObject move x y) |
[MSG] (ogre.lsh) |
4.4.1.4. (==> VisualObject moverel xr yr) |
[MSG] (ogre.lsh) |
4.4.1.5. (==> VisualObject resize w h) |
[MSG] (ogre.lsh) |
4.4.1.6. (==> VisualObject geometry x y w h) |
[MSG] (ogre.lsh) |
Method geoemtry is normally called by the method manage-geometry defined by the container objects for enforcing a particular layout. You may call this method directly (this is a backward compatibility requirement), although it is more efficient to use method moveresize .
4.4.1.7. (==> VisualObject front x y w h) |
[MSG] (ogre.lsh) |
4.4.1.8. (==> VisualObject back x y w h) |
[MSG] (ogre.lsh) |
4.4.2. VisualObject Implementation Methods |
4.4.2.0. (==> VisualObject realize window) |
[MSG] (ogre.lsh) |
The default definition of method realize is given below.
(defmethod visualobject realize (w) (when (setq window w) (==> this compute-geometry) (==> itscontainer change-geometry) ) (==> this expose rect) )
Subclasses of VisualObject may override this default definition. The new definition however must call the superclass method realize in order to perform the essential tasks described above.
Before overriding method realize , you should also consider overriding method compute-geometry instead of method realize .
4.4.2.1. (==> VisualObject compute-geometry) |
[MSG] (ogre.lsh) |
This method must either return an empty list or compute the minimal size of the object's rectangle and enforce this minimal size by sending a message resize and return the new rectangle rect .
The default method compute-geometry just returns the empty list. Class String for instance overrides this method to compute the size of the string text and resizes the string object to the correct size.
4.4.2.2. (==> VisualObject backpaint) |
[MSG] (ogre.lsh) |
The default method backpaint tests wether you have a black and white or a color display and calls method backpaint-bw or backpaint-color respectively. It is therefore advisable to override methods backpaint-bw and backpaint-color instead of backpaint .
4.4.2.3. (==> VisualObject backpaint-bw) |
[MSG] (ogre.lsh) |
The default backpaint-bw method just clears the object's rectangle with the background color color-bg . This is suitable for most cases.
4.4.2.4. (==> VisualObject backpaint-color) |
[MSG] (ogre.lsh) |
The default backpaint-color method just clears the object's rectangle with the background color defined in the current palette. This is suitable for most cases.
4.4.2.5. (==> VisualObject repaint) |
[MSG] (ogre.lsh) |
The default method repaint tests wether you have a black and white or a color display and calls method repaint-bw or repaint-color respectively. It is therefore advisable to override methods repaint-bw and repaint-color instead of repaint .
4.4.2.6. (==> VisualObject repaint-bw) |
[MSG] (ogre.lsh) |
The default method repaint-bw does nothing.
4.4.2.7. (==> VisualObject repaint-color) |
[MSG] (ogre.lsh) |
The default method repaint-color does nothing.
4.4.3. Event Methods |
When the Ogre library detects an event in a window, it builds an ordered list of objects eligible for handling the event. An event message is then send to the object with highest priority. If the event method returns the symbol ignored , the library proceed with the next object in the ordered list. This process stops as soon as an object accepts the event (i.e. until an event message returns a value different from symbol ignored .)
Starting with the highest priority, the objects eligible for receiving event messages are:
Class VisualObject defines a default method for the event messages. These default methods just reject the event by returning symbol ignored .
In practice, the default event rejection mechanism ensures that mouse events are handled by the object located below the mouse cursor and keyboard events are handled by the actived objects. It is however possible to change these settings by overriding the event methods of objects with a higher priority.
4.4.3.0. (==> VisualObject mouse-down x1 y1) |
[MSG] (ogre.lsh) |
You can use function eventinfo to find the name of the mouse button and the state of the shift and control keys. Names returned by function eventinfo are quite machine dependent however.
4.4.3.1. (==> VisualObject mouse-drag x1 y1 x2 y2) |
[MSG] (ogre.lsh) |
4.4.3.2. (==> VisualObject mouse-up x1 y1 x2 y2) |
[MSG] (ogre.lsh) |
4.4.3.3. (==> VisualObject keypress c x y) |
[MSG] (ogre.lsh) |
4.4.3.4. (==> VisualObject arrow-left x y) |
[MSG] (ogre.lsh) |
You can use function eventinfo to find the exact name of the key and the state of the shift and control keys. Key names returned by function eventinfo are quite machine dependent however.
4.4.3.5. (==> VisualObject arrow-right x y) |
[MSG] (ogre.lsh) |
You can use function eventinfo to find the exact name of the key and the state of the shift and control keys. Key names returned by function eventinfo are quite machine dependent however.
4.4.3.6. (==> VisualObject arrow-up x y) |
[MSG] (ogre.lsh) |
You can use function eventinfo to find the exact name of the key and the state of the shift and control keys. Key names returned by function eventinfo are quite machine dependent however.
4.4.3.7. (==> VisualObject arrow-down x y) |
[MSG] (ogre.lsh) |
You can use function eventinfo to find the exact name of the key and the state of the shift and control keys. Key names returned by function eventinfo are quite machine dependent however.
4.4.3.8. (==> VisualObject help x y) |
[MSG] (ogre.lsh) |
The help key under Windows is function key F1. The help key under X11 can be configured using program xmodmap .
You can use function eventinfo to find the exact name of the key and the state of the shift and control keys. Key names returned by function eventinfo are quite machine dependent however.
4.4.3.9. (==> VisualObject fkey x y) |
[MSG] (ogre.lsh) |
Since key names returned by function eventinfo are quite machine dependent, your event handling procedure should use this name as a hint rather than expecting well defined values.
Remark: All operating systems define certain hot keys for various purposes. Lush cannot override these assignments. Under Windows for instance, keys F9, F10 and CTRL-F4 are directly processed by the operating system and never passed to WinLush.
4.4.3.10. (==> VisualObject size w h) |
[MSG] (ogre.lsh) |
4.5. Control Objects |
Since class Control is a subclass of class VisualObject , all the properties and methods of class VisualObject also apply to class Control . In addition, class Control provides more support for defining the interactive graphics objects.
4.5.0. (new Control w h f) |
[CLASS] (ogre.lsh) |
This constructor is seldom used for creating new instances. Derived classes (e.g. Button ) usually call this constructor method within their own constructor method in order to initialize the Control part of their instances.
4.5.1. Enabling or Disabling a Control Object |
The enabled/disabled status of a control object is stored in the slot disabled defined by class Control .
Implementation methods usually test slot disabled before performing their task. Repainting methods must change the rendering colors according to the enabled/disabled status of the object is disabled. Event methods must ignore event messages when the object is disabled.
Two request methods, enable and disable , are defined by class Control for changing the enabled/disabled status of an object:
4.5.1.0. (==> Control disable) |
[MSG] (ogre.lsh) |
4.5.1.1. (==> Control enable) |
[MSG] (ogre.lsh) |
The disabled count feature proves useful for disabling an object either permanently or temporarily:
4.5.1.2. (new DisableLock c1...cn) |
[CLASS] (ogre.lsh) |
If however an error occurs during the program execution, the button would remain disabled. The best way to solve this problem consists in creating a lock object defined by class DisableLock .
If you wish to temporarily disable object c1 to cn , proceed as follows:
(let ((lock (new DisableLock c1...cn))) ;;; call your Lush program here (......) )
When created, the lock object sends a message disable to the objects c1 to cn . The lock is destroyed when you leave the let instruction. The lock destructor then sends a messages enable to our objects c1 to cn .
If an error occurs during the execution of the Lush program, the lock is destroyed by the garbage collector and the objects retrived to their initial state.
4.5.2. Activation of a Control Object |
Activation is especially useful for redirecting keyboard events toward objects containing some editable text. Having the keyboard events processed by the object located under the mouse often seems unnatural because the keyboard does not move physically like a mouse.
The user activates such an editable object with a mouse click in the object rectangle. It is customary in Ogre that the editable objects ignore the keyboard events unless they are active. Most keyboard events are then unconditionnally directed to the active object without regard to the position of the mouse cursor.
On the other hand, it is often useful to implement accelerator keys for performing tasks usually achieved by menu items or buttons. Accelerator key events must be defined by the containers managing the related objects. This definition ensures that accelerator keys events are not sent to the active object but handled directly.
The activation status of a control object is stored in slot activated (sic) defined by class Control . This slot contains a non nil value if the control object is active. Implementation methods of editable objects usually test slot activated before performing their task. Repainting methods must indicate the activation status of an object. Event methods must ignore event messages unless the object is actived.
Two request methods are defined by class Control for testing or changing the activation status of an object. These methods ensure that one object only is actived in a given window.
4.5.2.0. (==> Control activate d) |
[MSG] (ogre.lsh) |
4.5.3. Appearance of a Control Object |
Two request methods are defined by class Control for obtaining and changing the contents of this slot:
4.5.3.0. (==> Control settext d) |
[MSG] (ogre.lsh) |
Since this change causes a general change in the object appearance, the object is sent a message compute-geometry for redefining its geometry requirements and a message expose for updating the display. Sending message settext sometimes triggers a global relocation of all objects in the window.
4.5.3.1. (==> Control gettext) |
[MSG] (ogre.lsh) |
4.5.4. State of a Control Object |
Three methods hasdata , setdata and getdata are used for obtaining and changing the state of a control object. These methods are more implementation method than request methods. Several subclasses of Control override these methods in order to pre-process or post-process the state information.
These methods are mostly useful because they provide an abstract way to save and restore the state of a collection of control objects. For instance, a container object can save or restore the state of all its descendants with a single message.
4.5.4.0. (==> Control setdata d) |
[MSG] (ogre.lsh) |
The default method sets the slot data of the target object to the value given by argument d . Since this change usually changes the object appearance, the object is sent a message expose for updating the display.
4.5.4.1. (==> Control getdata) |
[MSG] (ogre.lsh) |
The default method just returns the contents of slot data . It is prefered to use this method rather than accessing directly the slot data because complex control objects may use other slots for controlling the state of the object.
4.5.4.2. (==> Control hasdata) |
[MSG] (ogre.lsh) |
Method hasdata is used to test if an object provides an adequate implementation for methods setdata and getdata . The default method hasdata indicated that there is some state information in the control object. Certain subclasses, like push button, override this method in order to signal that they carry no state information.
4.5.5. Ogre Callbacks |
Callbacks functions are called with one argument which is the caller object. They are executed within the caller scope and therefore directly access the slots of the caller object. In addition certain local variables are set:
Three methods are implemented by class Control for handling callback functions.
4.5.5.0. (==> Control setcall f) |
[MSG] (ogre.lsh) |
4.5.5.1. (==> Control execute) |
[MSG] (ogre.lsh) |
4.5.5.2. (==> Control trigger) |
[MSG] (ogre.lsh) |
The default implementation defined by class Control just calls method execute . Subclasses of Control usually override method trigger by sending false event messages to the object.
4.6. Container Objects |
Since class Container is a subclass of class VisualObject , all the properties and methods of class VisualObject also apply for class Container . Class Container however defines new slots and new methods for handling container objects.
Subclasses of Container are very frequently defined for defining structuring container whose sons are arranged according to a certain layout. Classes Row and Column are examples of such subclasses.
4.6.0. (new Container x y w h ...contents...) |
[CLASS] (ogre.lsh) |
This constructor is seldom used for creating new instances. Derived classes (e.g. Row ) usually call this constructor method within their own constructor method in order to initialize the Control part of their instances.
4.6.1. Repainting |
During this operation, the clipping rectangle is the container object's rectangle. Only the portion of the sons which overlap the container's rectangle are rendered.
4.6.2. Inserting an Removing Objects |
4.6.2.0. (==> Container insert what) |
[MSG] (ogre.lsh) |
When an object is inserted into a container, the coordinates of the topleft corner of the container's rectangle are added to the coordinates of the object's rectangle. Therefore, if the initial rectangle of an object is located at cordinates (0, 0) , the object appears on the topleft corner of the container.
If there is a policy for the layout of the container, the new layout is computed and each object in the container is moved, resized and repainted.
4.6.2.1. (==> Container remove what) |
[MSG] (ogre.lsh) |
When an object removed from a container, the coordinates of the topleft corner of the container's rectangle are subtracted from the coordinates of the object's rectangle. Inserting the object again thus inserts the object at the same location with respect to the container's topleft corner.
4.6.2.2. (==> Container removall) |
[MSG] (ogre.lsh) |
4.6.3. Geometry Management |
These structuring containers make Ogre more attractive and easier to use than aksing the programmer to precompute the position of each object for all the interface configurations. Yet, geometry management is the most difficult component of the Ogre library.
Containers define the geometry policy using only two implementation methods, compute-geometry and manage-geometry . Here are the steps involved in a geometry computation:
The hidden part of the iceberg lies in the global computation of the object's location. If the container decides to change its geometry requirements during step 2, the container of the container then receives a message compute-geometry and gets a chance to participate to this geometry discussion. When this second container sets the first container's geometry, it calls the first container's method geometry which calls the first container's method manage-geometry and effectively performs step 3.
Important Note: The geometry management system has been significantly revamped in Lush. It is now faster and skinnier. Old programs still work correctly unless they redefine method geometry of class WindowObject .
4.6.3.0. (==> Container compute-geometry) |
[MSG] (ogre.lsh) |
This method must compute the minimal size of the container on the basis of the information stored in the slots rect of the managed objects or cached in the list of managed objects stored in slot contents of the container. This method may enforce a minimal size by sending a message resize .
The default method compute-geometry just returns the empty list.
Example:
Class Row defines the following method compute-geometry which computes the minimal size of a row:
(defmethod row compute-geometry () (when window (let* ((height (sup (cons 0 (all (((son x y w h) contents)) h)))) (width (sum (all (((son x y w h) contents)) w))) ) (==> this resize (+ width (* hspace (length contents))) height) rect ) ) )
4.6.3.1. (==> Container manage-geometry) |
[MSG] (ogre.lsh) |
This method must compute the final rectangle of all the managed objects according to the container's rectangle (found in slot rect of the container) and according to the managed object initial rectangles (found in slot rect of the managed objects and cached in the list of managed objects located in slot contents of the container.) Method manage-geometry then sends a message geometry to the managed objects in order to assign a final rectangle to these objects.
The default method manage-geometry defined in class Container does nothing. A secondary mechanism just moves the managed objects in order to maintain their position with respect to the topleft corner of the container's rectangle.
Example:
Class Row defines the following method manage-geometry which arranges the managed objects in a row starting on the left of the container's rectangle.
(defmethod row manage-geometry () (let (((x y w h) rect)) (incr x (div hspace 2)) (each ((i contents)) (let ((w (nth 3 i))) ;; get the object's width (==> (car i) geometry x y w h) (incr x (+ w hspace)) ) ) ) )
4.6.3.2. (new GeometryLock c) |
[CLASS] (ogre.lsh) |
Such a task is accomplished by creating an object instance of class GeometryLock as follows:
(let ((lock (new GeometryLock c))) ;;; insert your objects here into container c (==> c insert ...) )
When created, the lock sets a flag in container c which suspend the usual geometry management. When the lock is destroyed, the flag is cleared and the geometry management is started once. If an error occurs during the object insertion, the lock is destroyed by the garbage collector and the container is left with an acceptable state.
4.6.4. Control Management |
4.6.4.0. (==> Container disable) |
[MSG] (ogre.lsh) |
(defmethod container disable () (each ((i contents)) (==> (car i) disable) ) )
4.6.4.1. (==> Container enable) |
[MSG] (ogre.lsh) |
(defmethod container enable () (each ((i contents)) (==> (car i) enable) ) )
4.6.4.2. (==> Container hasdata) |
[MSG] (ogre.lsh) |
(defmethod container hasdata () (flatten (all ((i contents)) (==> (car i) hasdata))) )
4.6.4.3. (==> Container getdata) |
[MSG] (ogre.lsh) |
(defmethod container getdata () (all ((i (==> this hasdata))) (==> i getdata) ) )
4.6.4.4. (==> Container setdata d) |
[MSG] (ogre.lsh) |
(defmethod container setdata (d) (each ((i (==> this hasdata)) (j d) ) (==> i setdata j) ) )
4.7. Container Flavors |
4.7.0. Forms |
Class Form is a trivial subclass of class Container . Class Form adds three properties to standard containers.
These properties are handy for designing complex graphics objects composed of several components as subclasses of Form . Several classes, like class WindowObject , class Requester and class Viewer , are implemented as subclasses of Form .
Moreover, a form object is sometimes considered as an extended control object. For instance, we can gather a slider and an editable numeric field into a form object and use this object like an usual control object.
This is achieved by defining a subclass of Form whose constructor builds the elements of the composite object and keeps a pointer to these object in some of its slots. This subclass then must define methods hasdata , setdata , getdata , execute and setcall like those of a control object. Method hasdata usually returns this . The other methods are usually forwarded to the components of the composite object.
4.7.0.0. (new Form ...contents...) |
[CLASS] (ogre.lsh) |
4.7.1. Window Objects |
A window object is a toplevel container. Inserting a window object into another container is an almost certain cause of trouble.
Whole graphics interface are often defined as subclasses of WindowObject . Such subclasses usually contain new slots for storing interface specific data. Callback functions then send messages to thiswindowobject . These messages then are executed within the interface scope.
4.7.1.0. (new WindowObject x y w h name ...contents...) |
[CLASS] (ogre.lsh) |
When a window object is created, the constructor creates a window on the screen named name with width w and height h . If both arguments x and y are 0 , the position of this window is defined by the default rule of your computer. If both arguments are positive, they define the position of the topleft corner of the window.
Exemple:
;;; First of all, initialize Ogre ! ? (ogre) = idle-hook ;;; create a window with a button ? (setq win (new WindowObject 100 100 300 300 "Example" (setq b (new stdbutton "Hello" ;; its name (lambda(c) (beep)) )) )) ;; its callback = ::WindowObject:06f00 ;;; Now we remove the button b from win ? (==> win remove b)
Note: The event handler of the newly created window is the corresponding Ogre window object. You can thus interactively obtain the Ogre window object for a given window by typing (setq w (waitevent)) and clicking into the corresponding window.
4.7.1.1. (new AutoWindowObject x y w h name contents) |
[CLASS] (ogre.lsh) |
The constructor arguments w and h are only hints for sizing the window. They may be empty lists instead of numbers. The size of the window is eventually given by the size of its contents.
4.7.1.2. WindowObject Request Methods |
4.7.1.2.0. (==> windowobject palette p) |
[MSG] (ogre.lsh) |
Argument p can be the empty list (for black and white display) or a palette returned by function new-palette (for color display). The color palette selection is enforced even if you do not use an adequate screen.
4.7.1.2.1. (==> windowobject setmodal m) |
[MSG] (ogre.lsh) |
4.7.1.2.2. (==> windowobject getmodal) |
[MSG] (ogre.lsh) |
4.7.1.2.3. (==> windowobject read-event) |
[MSG] (ogre.lsh) |
4.7.1.2.4. (==> windowobject manage-event event) |
[MSG] (ogre.lsh) |
4.7.1.3. WindowObject Implementation Methods |
4.7.1.3.0. (==> windowobject delete) |
[MSG] (ogre.lsh) |
The default method delete just deletes the window object. You may override this method, for instance, to pop up a confirmation dialog to the user.
4.7.1.3.1. (==> windowobject manage-geometry) |
[MSG] (ogre.lsh) |
The default method manage-geometry does nothing. Overriding the method manage-geometry in a window object is often useful for adjusting the size of the window object components to the onscreen window size.
4.7.1.3.2. (==> windowobject compute-geometry) |
[MSG] |
The default method compute-geometry does nothing. You may override this method to adjust automatically the size of the onscreen window to the mimial size of its contents.
4.7.2. Structuring Containers |
These containers inherit all the slots and methods of an ordinary container. They just define specific methods compute-geometry and manage-geometry to enforce their layout policy.
In addition, two classes are provided for padding a row or a column with some space, class EmptySpace and class DarkSpace .
4.7.2.0. (new Row ...contents...) |
[CLASS] (ogre.lsh) |
Class Row defines a structuring container which aligns its contents horizontally from left to right. The height of a row is determined by the managed object whose required height is largest.
An empty space is inserted between the objects. This space is defined by slot hspace of the row object and defaults to 4 points.
4.7.2.1. (new Column ...contents...) |
[CLASS] (ogre.lsh) |
Class Column defines a structuring container which aligns its contents vertically from top to bottom. The width of a column is determined by the managed object whose required width is largest.
An empty space is inserted between the objects. This space is defined by slot vspace of the column object and defaults to 4 points.
Example:
;; Creates a window with three buttons ? (setq win (new WindowObject 100 100 300 300 "Essai" (new Column (new StdButton "One" (lambda(d) (print 1))) (new StdButton "Two" (lambda(d) (print 2))) (new StdButton "Three" (lambda(d) (print 3)))))) = ::WindowObject:06f00
4.7.2.2. (new Grid cols ...contents...) |
[CLASS] (ogre.lsh) |
Class Grid defines a structuring container which aligns its contents in a grid with cols columns. The grid is filled left to right. The width of each column is determined by the object of the column whose required width is largest. The height of each row is determined by the object of the row whose required height is largest.
Empty spaces are inserted between rows and columns. These spaces are defined by slot vspace and hspace of the grid object and defaults to 4 points.
4.7.2.3. (new EmptySpace w [h] ) |
[CLASS] (ogre.lsh) |
Class EmptySpace is a class of emptyspace objects. The constructor expression above returns a new emptyspace object of minimal width w and minimal height h . When argument h is omitted, a square minimal space is assumed.
Of course, the minimal geometry specifications interact with the geometry management of structuring conteners. It is seldom necessary to define all the sizes of an emptyspace object.
Exemple:
;;; Creates a window with three buttons ;;; with a 20 pixels space between button 2 and button 3 ? (setq win (new WindowObject 100 100 300 300 "Essai" (new Column (new StdButton "One" (lambda(d) (print 1))) (new StdButton "Two" (lambda(d) (print 2))) (new EmptySpace 20) (new StdButton "Three" (lambda(d) (print 3))) ))) = ::WindowObject:06f00
4.7.2.4. (new DarkSpace w [h] ) |
[CLASS] (ogre.lsh) |
Class DarkSpace is a class of emptyspace objects. The constructor expression above returns a new emptyspace object of minimal width w and minimal height h . When argument h is omitted, a square minimal space is assumed.
Of course, the minimal geometry specifications interact with the geometry management of structuring conteners. Specifying a single argument of 4 , for instance, will define a minimal width and height of 4 points. If such a darkspace object is inserted in a column, the width of object will be increased up to the column width, effectively displaying a 4 points thick line.
Exemple:
;;; Creates a window with three buttons ;;; with a 3 pixels line between button 2 and button 3 ? (setq win (new WindowObject 100 100 300 300 "Essai" (new Column (new StdButton "One" (lambda(d) (print 1))) (new StdButton "Two" (lambda(d) (print 2))) (new DarkSpace 3) (new StdButton "Three" (lambda(d) (print 3)))))) = ::WindowObject:06f00
4.8. Buttons |
All these classes are subclasses of Control . Buttons thus inherit all the slots and methods defined in class Control .
4.8.0. Push Buttons |
4.8.0.0. (new StdButton label call) |
[CLASS] (ogre.lsh) |
The constructor expression returns a push button whose name is given by string label . This label may be changed by sending a message settext . When the button is depressed, the callback function call is executed. The button remains disabled while the callback function has not returned.
Example:
;;; A window with a 3 state label ? (setq win (new WindowObject 100 100 300 300 "Essai" (new StdButton "One" (lambda(caller) (==> caller settext (selectq (==> caller gettext) ("One" "Two") ("Two" "Three") (t "One") ) ) ) ) ) ) = :WindowObject:06f00
4.8.0.1. (new TinyButton label call) |
[CLASS] (ogre.lsh) |
The constructor expression returns a push button whose name is given by string label . This label may be changed by sending a message settext . When the button is depressed, the callback function call is executed. The button remains disabled while the callback function has not returned.
4.8.1. Check Boxes |
Class CheckBox implements check box buttons. Since class CheckBox is a subclass of class Control , all the slots and methods defined for control objects are inherited.
4.8.1.0. (new CheckBox label call) |
[CLASS] (ogre.lsh) |
You can query or change the state of a check box ( t or () ) by sending messages getdata and setdata . The descriptive text can be modified using settext .
Example:
;;; A window with a check box that controls ;;; the enable/disable state of a push button ? (setq win (new WindowObject 100 100 300 300 "Essai" (new column (setq thebutton (new StdButton "beep" (lambda(c) (beep)) )) (new CheckBox "Disable button" (lambda(c) (if (==> c getdata) (==> thebutton disable) (==> thebutton enable) ) ) ) ) ) )
4.8.1.1. (new ImageButton up-image down-image disabled-image call) |
[CLASS] (ogre.lsh) |
Here is an example that shows how to create down and disabled image of an ImageButton from an up image.
(libload "libimage/image-io") (libload "libimage/rgbaimage") (ogre) (setq up (image-read-rgba "my-button-image.png")) (setq down (idx-copy up)) (rgbaim-contbright up down -1 0) (setq disabled (idx-copy up)) (rgbaim-contbright up disabled 0.5 40) (setq btn (new ImageButton up down disabled (lambda (c) (printf "coucou\n")))) (setq window (new windowobject 10 10 200 200 "test" btn))
4.8.1.2. (new StdButtonBg text up-image down-image disabled-image call) |
[CLASS] (ogre.lsh) |
Here is an example that shows how to create an StdButtonBg from one of the standard icons:
(libload "libimage/image-io") (libload "libimage/rgbaimage") (setq icondir (concat-fname lushdir "lsh/libogre/icons")) (ogre) (setq btn (new StdButtonBg "Click Me" (image-read-rgba (concat-fname icondir "button-brushed-metal-02-up.png")) (image-read-rgba (concat-fname icondir "button-brushed-metal-02-down.png")) (image-read-rgba (concat-fname icondir "button-brushed-metal-02-disabled.png")) (lambda (c) (printf "coucou\n")))) (setq window (new windowobject 10 10 200 200 "test" btn)) (==> btn disable) (==> btn enable)
4.8.2. Exclusive Buttons |
Class RadioButton implements exclusive buttons. Since class RadioButton is a subclass of class CheckBox , all the slots and methods defined for checkbox and control objects are inherited.
4.8.2.0. (new RadioButton label call) |
[CLASS] (ogre.lsh) |
You must insert a radiobutton in a container containing only radiobuttons. Whenever the user clicks on a radiobutton, the state of all the other radiobuttons in the container becomes () , while the state of the clicked radiobutton becomes t and the callback function call is called.
Example:
;;; A window with a three radio buttons ;;; building a three state choice ? (setq win (new WindowObject 100 100 300 300 "Essai" ;; create a common callback function (let ((call (lambda(d) (printf "%s has been selected\n" (==> d gettext) ) ))) (new column (new radiobutton "choice 1" call) (new radiobutton "choice 2" call) (new radiobutton "choice 3" call) ) ) ) )
Remarks:
4.8.3. File Requester Button |
A file requester button is usually associated with a string editor, which displays the file selected in the requester.
See: File Requester.
See: Editable Strings.
4.8.3.0. (new FileReqButton areqmsg aedstring) |
[CLASS] |
4.9. Character String Objects |
4.9.0. Fixed Strings |
4.9.0.0. (new String text) |
[CLASS] (ogre.lsh) |
The text is displayed using a font set by the fonction stored in slot textfont of the string object. This font default to a standard 12 points font.
4.9.1. Editable Strings |
An editable string object becomes actived when the user clicks the mouse button above its rectangle. Keypress and arrow events then are bound to editing functions according to a keymap stored in global variable EditStringKeymap .
The text edited by an editable string is controlled by the value of the slot regex of the editable string. This slot contains a regular expression which must match the text at all times. If an editing action results in a non-matching text, the action is discarded and a beep is emited.
4.9.1.0. (new EditString minsize [defaulttext]) |
[CLASS] (ogre.lsh) |
Example:
;;; A window with two editable strings ? (setq win (new WindowObject 100 100 300 300 "Essai" (new grid 2 (new string "First string:") (new editstring 16 "number one") (new string "Second string:") (new editstring 16 "number two") ) ) )
4.9.1.1. EditStringKeymap |
[VAR] (ogre.lsh) |
Every typed character is matched against the keymap.
The default keymap is loosely modeled after the Emacs text editor:
(setq EditStringKeymap '( ("\n" execute) ;; <LineFeed> ("\r" execute) ;; <Enter> or <Return> ("\b" backspace ;; <ctrl-H> or <Backspace> ("\x7f" backspace) ;; <Delete> ("\^D" delete-char) ;; <Ctrl-D> ("Delete" delete-char) ;; Key <Delete> ("\^A" begin-of-line) ;; <Ctrl-A> ("Home" begin-of-line) ;; Key <Home> ("\^E" end-of-line) ;; <Ctrl-E> ("End" end-of-line) ;; Key <End> ("\^F" arrow-right) ;; <Ctrl-F> and <Right> ("\^B" arrow-left) ;; <Ctrl-B> and <Left> ("\^P" arrow-up) ;; <Ctrl-P> and <Up> ("\^N" arrow-down) ;; <Ctrl-N> and <Down> ("\^K" kill) ;; <Ctrl-K> ("\^Y" yank) ) ) ;; <Ctrl-Y>
The binding second elements, actions, are the names of methods defined by class EditString to perform the various editing tasks. The valid actions are:
4.9.2. Editable Numbers |
4.9.2.0. (new EditNumber minsize [defaultvalue]) |
[CLASS] (ogre.lsh) |
Argument minsize is the minimal number of characters dislayed in the object. The initial value of the number is specified by the optional number argument defaultvalue .
When sent to an editable numeric string, message setdata requires a numerical argument. Similarly, message getdata returns a number or the empty list when no text has been entered.
4.9.3. EditSecretString |
4.9.4. Multiline Text Editor |
4.9.4.0. (new EditText w h [default]) |
[CLASS] (ogre.lsh) |
Class EditText is a subclass of Forms that defines composite object containing a TextPane object and two scroll bars. The text editor is actually implemented by class TextPane described hereafter.
Class EditText forwards messages
setdata , getdata ,
read-file and write-file to
this underlying TextPane object.
See: (new TextPane w
h [ def
editp vs
hs ])
See: (==> TextPane
getdata)
See: (==> TextPane
setdata arg )
See: (==> TextPane
read-file fname )
See: (==> TextPane
write-file fname )
4.9.4.1. (new TextPane w h [def editp vs hs]) |
[CLASS] (ogre.lsh) |
4.9.4.2. TextPaneKeymap |
[VAR] (ogre.lsh) |
(setq TextPaneKeymap '( ("\n" execute) ("\r" execute) ("\b" backspace) ("\x7f" backspace) ("\^A" begin-of-line) ("\^B" arrow-left) ("\^D" delete-char) ("\^E" end-of-line) ("\^F" arrow-right) ("\^K" kill) ("\^N" arrow-down) ("\^P" arrow-up) ("\^V" page-down) ("\^Y" yank) ("Delete" delete-char) ("C-Home" begin-of-text) ("C-End" end-of-text) ("Home" begin-of-line) ("End" end-of-line) ("Prior" page-up) ("Next" page-down) ("\x1b" metakey "\x1b") ("\x1b<" begin-of-text) ("\x1b>" end-of-text) ("\x1bv" page-up) ) )
The first element of each association describes the keystroke or keystroke combination. This string can contain an ASCII character, a string containing a keystroke combination, or a function key name. The leading character of keystroke combinations must be associated to method metakey . The function key names may be preceded by "C-" and "S-" to indicate that the control or shift key must be depressed.
The rest of the association specify which action should be called when the corresponding keystroke is typed by the user. The following actions are supported:
4.9.4.3. (==> TextPane setdata arg) |
[MSG] (ogre.lsh) |
4.9.4.4. (==> TextPane getdata) |
[MSG] (ogre.lsh) |
4.9.4.5. (==> TextPane read-file fname) |
[MSG] (ogre.lsh) |
4.9.4.6. (==> TextPane write-file fname) |
[MSG] (ogre.lsh) |
4.10. Icons |
4.10.0. (new icon mat &optional [sx [sy [map]]]) |
[CLASS] |
Argument mat is a 2D matrix containing the data to be represented as rectangles using 64 gray levels. By default, the values lower than 0 will be black and the values beyond 1 will be white.
Arguments sx and sy are the width and height of each rectangle.
Argument cmap may be a 1D integer matrix defining 64 colors. When it is defined, its colors are used instead of gray levels.
See: (gray-draw-matrix x
y mat
minv maxv
apartx aparty )
See: (color-draw-matrix x
y mat
minv maxv
apartx aparty
cmap )
4.11. OgrImage |
4.11.0. (new OgrImage m) |
[CLASS] |
4.11.1. (==> OgrImage get-selected) |
[MSG] |
4.12. ImageViewer |
4.12.0. (new ImageViewer w h m [scroll]) |
[CLASS] |
(libload "libimage/image-io") (setq m (image-read-rgb (concat-fname lushdir "lsh/libimage/demos/sample.jpg"))) (ogre) (setq w (new WindowObject 10 10 400 400 "asd" (new ImageViewer 340 340 m t)))
4.12.1. (==> ImageViewer get-selected) |
[MSG] |
4.13. Menus |
Menus are implemented with three classes:
The constructor of class Menu sets up all these objects in a single call.
4.13.0. Standard Menus |
4.13.0.0. (new Menu menuname label1 call1 ... labelN callN ) |
[CLASS] (ogre.lsh) |
Example:
;;; A window with a menu composed of 3 items ? (setq win (new WindowObject 100 100 300 300 "Essai" (new menu "Test Menu" ;; the first item toggles its check mark "Toggle" (lambda(item) (==> item setdata (not (==> item getdata)))) ;; the second item activates the bell "Bell" (lambda(item) (beep)) ;; the third one destroy this interface ;; remenber that <thiswindowobject> always ;; refer to the closest window object. "Quit" (lambda(item) (==> thiswindowobject delete)) ) ) )
4.13.0.1. (==> Menu finditems label) |
[MSG] (ogre.lsh) |
4.13.0.2. (==> Menu disable ...labels...) |
[MSG] (ogre.lsh) |
Each argument of message disable is interpreted like the argument label of message finditems. It can be the empty list, a number or a string.
4.13.0.3. (==> Menu enable ...labels...) |
[MSG] (ogre.lsh) |
Each argument of message disable is interpreted like the argument label of message finditems. It can be the empty list, a number or a string.
4.13.0.4. (new MenuItem label call) |
[CLASS] (ogre.lsh) |
4.13.0.5. (==> Menu insert menuitem) |
[MSG] (ogre.lsh) |
4.13.0.6. (==> Menu remove menuitem) |
[MSG] (ogre.lsh) |
4.13.1. Choice Menus |
Choice menus supports the methods of standard menus, in addition to their own methods.
4.13.1.0. (new ChoiceMenu [items [callback]]) |
[CLASS] |
4.13.1.1. (==> choicemenu setitems items) |
[MSG] |
4.13.1.2. (==> choicemenu setdata d) |
[MSG] |
4.13.1.3. (==> choicemenu getdata) |
[MSG] |
4.13.1.4. (==> choicemenu setcall callback) |
[MSG] |
4.13.1.5. (==> choicemenu disable) |
[MSG] |
4.13.1.6. (==> choicemenu enable) |
[MSG] |
4.14. Popup Requesters |
Requesters are useful for asking the user to provide additional information about an action trigerred by a button or a menu item. The callback function of the button or menu item then just send a message popup to the requester which performs all the remaining tasks.
4.14.0. Requester |
Example:
;;; A window with a button that pops a requester up. ;;; The requester contains an editstring and two buttons. ;;; 1- Create the window ? (setq win (new WindowObject 100 100 300 300 "Essai" (setq thebutton (new StdButton "Pop the requester up" (lambda(d) (==> therequester popup)) )))) = ::WindowObject:06f00 ;;; 2- Create the requester ? (setq therequester (new Requester win (new Column ;; The first button pops the requester down ;; Variable <thisrequester> always refers to the closest (new StdButton "Pop the requester down" (lambda(d) (==> thisrequester popdown)) ) ;; The second button changes the label of the popup button (new EditString 8 "Pop it up again") (new StdButton "Change label" (lambda(d) (==> thebutton settext ;; Collective <getdata> on the requester! (car (==> thisrequester getdata))))))))) = ::Requester:0702c
4.14.0.0. (new Requester support ...contents...) |
[CLASS] (ogre.lsh) |
4.14.0.1. (==> Requester popup) |
[MSG] (ogre.lsh) |
All event messages are then directed to the requester. Since no other component of the interface toplevel is active, it is advisable to insert a button whose action consists in sending message popdown to the requester itself.
4.14.0.2. (==> Requester popdown) |
[MSG] (ogre.lsh) |
4.14.0.3. (==> Requester popuplock) |
[MSG] (ogre.lsh) |
Example:
;;; Pop up the requester defined above in this section and block: ? (==> therequester popuplock) ;;; Lush only returns when you activate the 'popdown' button = ()
4.14.0.4. (==> Requester popuphard) |
[MSG] (ogre.lsh) |
While message popuphard is active, no other window object accepts user events. If the user click in another Ogre window, the computer beeps and put the window containing the requester above all other window on the screen.
Message popuphard is useful for requesting an information which requires immediate attention from the user.
4.14.0.5. (==> Requester setsupport win) |
[MSG] (ogre.lsh) |
4.14.1. Warning Requester |
4.14.1.0. (new WarningRequester support) |
[CLASS] (ogre.lsh) |
4.14.1.1. (==> WarningRequester popup text) |
[MSG] (ogre.lsh) |
Using a warning requester with messages popup and popdown allows for displaying messages indicating what is being computed.
4.14.1.2. (==> WarningRequester popuplock seconds text) |
[MSG] (ogre.lsh) |
Using a warning requester with popuplock allows for displaying a temporary warning message.
4.14.2. Error Requester |
Such a requester is very useful for signaling an error to the user. The user must depress the button "Ok" to remove the requester. This button actually sends a message popdown to the error requester.
Example:
;;; Assuming window object <win> already exists: ? (setq error-dialog (new ErrorRequester win)) = ::ErrorRequester:07120 ? (==> error-dialog popup "Error message") = ()
4.14.2.0. (new ErrorRequester support) |
[CLASS] (ogre.lsh) |
4.14.2.1. (==> WarningRequester popup text) |
[MSG] (ogre.lsh) |
Using a warning requester with messages popup and popdown allows for displaying messages indicating what is being computed.
4.14.2.2. (==> WarningRequester popuplock text) |
[MSG] (ogre.lsh) |
4.14.3. Yes/No Requester |
Like usual requesters, yes/no requesters are popped up when they receive a message popup or popuplock . Both the positive and the negative button pop the yes/no requester down.
When you create the yes/no requester however, you specify a callback function.
If you have popped up the requester using message popuplock , you can also test which button has been depressed by looking at the value returned by message popupdown . This value is the empty list if the negative button has been depressed.
Slots yesbutton and nobutton of a yes/no requester contain the positive and negative buttons. These buttons may be programmatically actived by sending them a message trigger .
4.14.3.0. (new YesNoRequester support dialog yes no call) |
[CLASS] (ogre.lsh) |
Argument dialog is the user defined part of the requester. String yes is the label of the positive button. String no is the label of the negative button. Argument call is a callback function or the empty list.
Example:
;;; A button that pops up a yesnorequester ;;; for changing its label... ? (setq win (new WindowObject 100 100 400 200 "Essai" (setq thebutton (new StdButton "Hello" (lambda(c) (==> theyesnoreq popup)) )))) = ::windowobject:07010 ? (setq theyesnoreq (new YesNoRequester win ;; The user defined part (new Column (new String "Change button label") (new EditString 8 "new label") ) ;; The yes and no labels " Ok " "Cancel" ;; The callback (lambda(caller) (==> thebutton settext (car (==> caller getdata)) )))))))) = ::yesnorequester:07040
4.14.3.1. (==> YesNoRequester settext yes no default) |
[MSG] (ogre.lsh) |
There is usually a default button indicated by a wider outline. This button is triggered if the user hits the carriage return key. This default button is usually the positive one, but may be changed with argument default of message settext .
4.14.3.2. (==> YesNoRequester setcall callback) |
[MSG] (ogre.lsh) |
4.14.3.3. (==> YesNoRequester ask yes no default) |
[MSG] (ogre.lsh) |
When such a requester receives message ask , it first sets the text of the string object to text and the button labels to yes and no . The default button is indicated by argument default .
The requester is then popped up using popuphard . The user can then press either the positive or the negative button. When the requester is popped down, message ask returns t or () according to the user answer.
Example:
;;; Creates a confirmation dialog in an exiting win <win>. ? (setq confirm-dialog (new YesNoRequester win (new string "msg") ; dummy message "yes" "no" ; dummy labels () ) ; no callback = ::yesnorequester:070c0 ? (==> confirm-dialog ask "Should I really do that" ; the message "Proceed" "Please don't" ; the button labels 'no ) ; the default ;;; Waiting for your answer... = t or ()
4.14.4. Print Requester |
4.14.4.0. (new PrintRequester w [callback]) |
[CLASS] (ogre.lsh) |
4.14.4.1. (==> PrintRequester getdata) |
[MSG] (ogre.lsh) |
4.14.5. File Requester |
Starting from the current filename, the selector displays the contents of the directory. A first click on a selector item selects a file or directory and copies its name in the editable filename string. Alternatively, the user can type a filename in the filename field. The button "Ok" is disabled if the user types an invalid filename.
This selection is validated by a second click, by pressing the enter key or by depressing the button "Ok" .
Class FileRequester is a subclass of class YesNoRequester which implements the standard Ogre file requester.
4.14.5.0. (new FileRequester w [message flag filter callback]) |
[CLASS] (ogre.lsh) |
Special behavior are selected by argument flag :
Argument filter is a function with one argument for testing the filenames. It returns a non nil value if its argument is a valid file name for this file request. For instance, this function might test an extension or examine the file header. Files rejected by the function filter are not displayed in the selector. If the user types such an invalid filename in the filename field, validation is prevented by disabling the button "Ok" .
4.14.5.1. (==> FileRequester setdata fname) |
[MSG] (ogre.lsh) |
4.14.5.2. (==> FileRequester getdata) |
[MSG] (ogre.lsh) |
4.14.5.3. (==> FileRequester getdir) |
[MSG] (ogre.lsh) |
4.14.5.4. (==> FileRequester getbase) |
[MSG] (ogre.lsh) |
4.14.5.5. (==> FileRequester setparm message flag [filter]) |
[MSG] (ogre.lsh) |
4.14.5.6. (==> FileRequester ask message flag [filter [fname]]) |
[MSG] (ogre.lsh) |
4.14.5.7. (ogre-ask-file winobj message flag [filter [fname]]) |
[DE] |
Other arguments are the same as for method ask of class FileRequester .
See: (==> FileRequester
ask message
flag [ filter [
fname ]])
4.15. Movable and Resizable Objects |
4.15.0. (new DragArea w h) |
[CLASS] (ogre.lsh) |
A dragging area appears as a rectangle with a gray outline. When you depress the mouse inside a dragging area, you can drag its container to another place.
Two slots affect the behavior of a dragging area:
4.15.1. (new SizeArea w h) |
[CLASS] (ogre.lsh) |
A sizing area appears as two overlapping gray squares. When you depress the mouse inside a sizing area, you can change the size of its container. Just drag the mouse until you touch a container boundary. This container boundary then follows the mouse until you release the mouse button.
Two slots affect the behavior of a size area.
4.15.2. (new Frame x y w h ...contents...) |
[CLASS] (ogre.lsh) |
Class Frame is a subclass of class Container . A frame always contains a dragging area located in the background of the container. A frame thus is a container that the user can move.
Example:
? (setq win (new windowobject 0 0 400 400 "Essai" (new Frame 50 30 200 140 (new stdbutton "Beep" (lambda(c) (beep)) ) ) ))
4.15.3. (new FrameSize x y w h ...contents...) |
[CLASS] (ogre.lsh) |
Class FrameSize is a subclass of class Frame . A sizable frame always contains a dragging area located in the background of the container. A sizable frame also contains a small size area in its bottom right corner. A sizable frame thus is a container that the user can move and resize.
Example:
? (setq win (new windowobject 0 0 400 400 "Essai" (new FrameSize 50 30 200 140 (new stdbutton "Beep" (lambda(c) (beep)) ) ) ))
4.16. Sliders and Scrollbars |
The user can grab and move the handle with the mouse. The user might also maintain the mouse button depressed on either side of the handle. In this case, the handle moves slowly towards the mouse pointer.
The standard callback function is called whenever the user releases the mouse button. An additional callback might be set up with message setdrag . This callback is called whenever the user moves the handle.
Sliders and scrollbars are implemented by a set of specialized subclasses of class Control .
Control abstract class for all controls Slider abstract class for all sliders and scrollbars HSlider horizontal sliders VSlider vertical sliders Scrollbar abstract class for all sliders and scrollbars HScrollbar horizontal scrollbars VScrollbar vertical scrollbars
Example: Example:
? (setq win (new windowobject 100 100 400 200 "Sliders & Scrollbars" (new row (new grid 2 (new emptyspace 100 100) (new vslider 0 100 ()) (new hslider 0 100 ()) (new emptyspace 10 10) ) (new grid 2 (new emptyspace 100 100) (new vscrollbar 100 ()) (new hscrollbar 100 ()) (new emptyspace 10 10) ) ) ) )
4.16.0. Sliders |
4.16.0.0. (new HSlider mini maxi [callback]) |
[CLASS] (ogre.lsh) |
4.16.0.1. (new VSlider mini maxi [callback]) |
[CLASS] (ogre.lsh) |
4.16.0.2. (==> Slider setrange mini maxi) |
[MSG] (ogre.lsh) |
4.16.0.3. (==> Slider setstep step) |
[MSG] (ogre.lsh) |
The initial increment is always 1. This initial increment ensures that the slider or scrollbar is limited to integer values. Specifying the empty list as an increment means that any value in the legal range are allowed.
4.16.0.4. (==> Slider setdrag call) |
[MSG] (ogre.lsh) |
4.16.1. Scrollbars |
Scrollbars are implemented by classes HScrollbar and VScrollbar which are indirect subclasses of class Slider . All the methods defined by class Slider are thus inherited by scrollbars.
4.16.1.0. (new HScrollbar maxi [callback]) |
[CLASS] (ogre.lsh) |
4.16.1.1. (new VScrollbar maxi [callback]) |
[CLASS] (ogre.lsh) |
4.16.1.2. (==> Scrollbar setrange mini maxi [prop]) |
[MSG] (ogre.lsh) |
4.17. Composite Objects |
4.17.0. Viewers |
Class Viewer implements viewer objects.
4.17.0.0. (new Viewer w h contents [hp vp]) |
[CLASS] (ogre.lsh) |
Example:
? (setq win (new WindowObject 0 0 400 200 "Essai" (new Viewer 300 150 (new column (new stdbutton "One" ()) (new emptyspace 60 60) (new stdbutton "Two" ()) (new stdbutton "Three" ()) (new stdbutton "Four" ()) ) t t) ) )
4.17.0.1. (==> Viewer setpos h v) |
[MSG] (ogre.lsh) |
4.17.0.2. (==> Viewer sethpos h) |
[MSG] (ogre.lsh) |
4.17.0.3. (==> Viewer setvpos v) |
[MSG] (ogre.lsh) |
4.17.0.4. (==> Viewer setcontenu object) |
[MSG] (ogre.lsh) |
4.17.1. Selectors |
Class Selector implements a selector object. The complete behavior of a selector is controlled by three slots of the selector object: multiple , call1 and call2 .
Two callback functions are called when the user selects items.
4.17.1.0. (new Selector nvisible [callback [items]]) |
[CLASS] (ogre.lsh) |
Argument callback of the constructor controls the values of these flags.
Argument items optionally gives a list of strings initially displayed in the selector.
4.17.1.1. (==> Selector getdata) |
[MSG] (ogre.lsh) |
4.17.1.2. (==> Selector getdatanum) |
[MSG] (ogre.lsh) |
4.17.1.3. (==> Selector setdata data) |
[MSG] (ogre.lsh) |
4.17.1.4. (==> Selector setpos pos) |
[MSG] (ogre.lsh) |
4.17.1.5. (==> Selector setitems items) |
[MSG] (ogre.lsh) |
Since the minimal size of a selector depends on the width of the largest string in the item list, sending message setitems can trigger a geometry negociation and readjust the location of all interface components.
4.18. A Complete Example |
4.18.0. The Class Browser "classtool" |
4.18.0.0. (classtool [cl]) |
[DE] (classtool.lsh) |
The class browser interface is composed of a menu and six selectors:
The top of the class browser contains a menu and an information string. The information string displays the number of slots, the number of inherited slots, the number of methods and the number of inherited methods for the current class. The name of the menu is always the name of the current class, displayed in large characters.
The menu itself contains four items:
4.18.1. The Program "classtool" |
<lushdir>/lib/classtool.lsh
The first executable line of file "classtool.lsh" initializes the Ogre library by calling function ogre . This is necessary to ensure that the Ogre class library is properly loaded and initialized.
(ogre)
Then we define a subclass c-classtool of class WindowObject . This class contains several slots for referencing the major components of the interface.
(defclass c-classtool windowobject the-menu ;; the menu the-string ;; the information string the-i-classes ;; the superclass selector the-i-slots ;; the inherited slots selector the-i-methods ;; the inherited methods selector the-classes ;; the subclass selector the-slots ;; the slots selector the-methods ;; the method selector the-error-requester ;; a signaling requester the-class-requester ;; the requester cl ) ;; the current class
We define then the constructor of class c-classtool . This constructor first calls the constructor of its superclass WindowObject and defines the contents of the window object. This very long call sets up the major components of the classtool interface.
All the interface is a single column which contains:
The menu defines the five items documented above. The callback
functions of the menu items do not perform very much. They merely send
an appropriate action message to the interface itself (acceded through
variable thiswindowobject ) or pop up
a suitable the requester.
(setq the-menu (new Menu "object" "Show Class" (lambda(c) (==> thiswindowobject display-action)) "Show Subtree" (lambda(c) (==> thiswindowobject subtree-action)) "Refresh" (lambda(c) (==> thiswindowobject refresh-action)) "Select" (lambda(c) (==> the-class-requester popup)) "Quit" (lambda(c) (==> thiswindowobject delete)) ) )
The grid also include three emptyspace object which specify a minimal size for the grid columns. This technique avoids troublesome geometry changes because it ensures that the selectors are already wide enough for displaying most names.
The constructor of class c-classtool
then adjust the font used in the menu title by directly poking into the
object slot textfont . It calls then
method compute-geometry to ensure that
the object size is adjusted for the new font.
(setq :the-menu:textfont font-18) (==> the-menu compute-geometry)
The constructor of class c-classtool then creates two requesters.
(setq the-error-requester (new ErrorRequester this) )
We poke a new regular expression in the slot
regex of the editable string in order to ensure that the text
typed by the user is a valid symbol.
(setq the-class-requester (new YesNoRequester this (new column (new String "Type a class name") (new DarkSpace 3) (let ((x (new EditString 20))) (setq :x:regex "[A-Za-z]?[-_|A-Za-z0-9]*") x) ) " Ok " "Cancel" (lambda(c) (==> thiswindowobject select-action)) ) ) )
Method setclass is then defined. This method collects the class information for the selected class and updates the information displayed in the selectors.
It first checks that its argument is a valid class and pops up the error requester if this check is negative.
(defmethod c-classtool setclass(c) (if (not (and c (classp c))) (==> the-error-requester popup "This is not a valid class")
If the check is positive, method setclass displays a message "working" in the message string and force an immediate display update using message repair-damaged .
(==> the-string settext "<<working>>") (==> this repair-damaged)
The menu title is then changed to the class name using method settext . Since the display update is delayed until all events are processed, this change becomes visible when all selectors are updated.
Method setclass then collects the class information into six lists: the subclass list ( cc ), the slot list ( cs ), the method list ( cm ), the superclass list ( ic ), the inherited slot list ( is ) and the inherited method list ( im ). We take a particular care of separating the various inherited classes by a dummy entry in the inherited lists.
This information is then loaded into the selectors using method setitems . The message string is then updated to the class statistics.
(==> the-classes setitems cc) (==> the-slots setitems cs) (==> the-methods setitems (sort-list cm >)) (==> the-i-classes setitems ic) (==> the-i-slots setitems is) (==> the-i-methods setitems im) (==> the-string settext (sprintf " %l: %d+%d slots, %d+%d methods" cn (length cs) isc (length cm) imc) ) ) ) ) )
We define then the various action methods which are called by the menu items, by the selectors, or by the class selection requester.
Method classes-action is called when the user selects a class in the subclass or superclass requester. This method just sends a message setclass to switch to the new class.
(defmethod c-classtool classes-action(c) (let ((cn (==> c getdata))) (==> this setclass (apply scope (list (named cn)))) ) )
Method methods-action is called when the user selects a method in the method selector or the inherited method selector. Method methods-action first makes sure that the user did not select a dummy entry used for separating the classes in the inherited method selector. It uses then function pretty-method to print the definition of the selected method.
(defmethod c-classtool methods-action(c) (let ((m (==> c getdata)) (cl cl)) (when (<> (left m 5) "=====") (setq m (named m)) (while (and cl ~(member m (methods cl))) (setq cl (super cl)) ) (when cl (==> this repair-damaged) (==> c setdata ()) (print) (pretty-method cl m) ) ) ) )
Method refresh-action is called by the menu item "Refresh" . It just calls method setclass on the current class (from slot cl ) in order to reload the class information.
(defmethod c-classtool refresh-action() (==> this setclass cl) )
Method display-action is called by the menu item "Show Class" . It just prints the class definition when adequate.
(defmethod c-classtool display-action() (if (not (super cl)) (beep) (print) (pprint (nconc (list 'defclass (classname cl) (classname (super cl))) (slots cl) )) ) )
Method subtree-action is called by the menu item "Show Subtree" . It just prints an indented list of the subclasses of the current class.
(defmethod c-classtool subtree-action() (let ((subtree (lambda(cl tb) (tab tb) (print (classname cl)) (each ((c (subclasses cl))) (subtree c (+ tb 2)) ) ) )) (print) (subtree cl 2) ) )
Method select-action is called when the class selection requester pops down. It gets the class name typed by the user (using getdata ), normalize the name and calls method setclass again.
(defmethod c-classtool select-action() (let ((cn (car (==> the-class-requester getdata)))) (if (regex-match "\\|.*\\|" cn) (setq cn (regex-subst "^\\|(.*)\\|$" "%0" cn)) (setq cn (downcase cn)) ) (==> this setclass (apply scope (list (named cn)))) ) )
We override then method keypress in order to define an accelerator key. Depressing the space bar pops up the class selection requester. All other keypress events are handled by the default method which just ignore these events.
(defmethod c-classtool keypress(c x y) (selectq c (" " (==> the-class-requester popup)) (t (==> this (windowobject . keypress) c x y)) ) )
Finally the hook function classtool creates an instance of class c-classtool and sets the initial class displayed in the browser
(de classtool( &optional (cl object) ) (when (symbolp cl) (setq cl (eval cl)) ) (when (not (classp cl)) (error t "Not a class" cl) ) (let ((w (new c-classtool))) (==> w setclass cl) ) )
In addition, an autoload function classtool is defined in file "stdenv.lsh" which loads file "classtool.lsh" and calls this function classtool .