Special cases.


How to manage Scrolls

Nowadays, Scrollbars are a very common feature in interfaces. The Tk library deals with them is sometimes very tricky. youtoo/Tk provides a simpler method to bind scrollbars and widgets.

The best way to show that is with a couple of examples:

Simple bindings.

This is the case for doing normal bindings between widgets and scrollbars.

In Tk the binding to a scrollbar requires a couple of callbacks that have to be set by the user. youtoo/Tk gives an encapsulation of that. The user do not have to know anything about the callbacks.

Example

(defun test-scrollbar ()
  (tk-wm "title" () "Test Scrollbar")
  (let ((text (tk-make-text () relief: "sunken" bd: "2"))
        (scroll-1 (tk-make-scrollbar ())))

    (eul-associate text scroll-1 'vertical)

    (tk-pack text side: "left")
    (tk-pack scroll-1 side: "right" fill: "y")
  )

  (Tk_MainLoop)
)

See eul-associate command for detailes information.

Special bindings

This is the case for more complicated bindings. Sometimes the user requires to do special actions when the scrollbar is modified. THe previous example only produce the modification of the view in the associated widget.

The next example will show how to produce the correspondent callbacks. This example shows how one scrollbar could be bound to a four listboxes. The movement of the scrollbar will produce the modification of the view in the four listboxes, "simultaneously".

The first two functions are necessary in order to control the scroll widget. The scroll widget will append two or three arguments to his command: function. Listboxes (and generally all the possible widgets that are able to carry a scrollbar) will append two arguments to its yscrollcommand: function.

Example

(defmodule tk_scrollbar2
  (syntax (syntax-1)
  import (level-1 tcl-tk)
   )
(deflocal *lb1* ())
(deflocal *lb2* ())
(deflocal *lb3* ())
(deflocal *lb4* ())
(deflocal *scroll* ())
(deflocal i 0)

(defun Fun-scroll-callback (type entry . units)
  (tk-yview *lb1* type entry units)
  (tk-yview *lb2* type entry units)
  (tk-yview *lb3* type entry units)
  (tk-yview *lb4* type entry units))

(defun Fun-listbox-callback (first-entry last-entry)
  (tk-scroll-set *scroll* first-entry last-entry))

(defun prove ()
  (setq *scroll* (tk-make-scrollbar () command: Fun-scroll-callback))
  (setq *lb1* (tk-make-listbox () relief: "sunken"
			       yscrollcommand: Fun-listbox-callback))
  (setq *lb2* (tk-make-listbox () relief: "sunken"
			       yscrollcommand: Fun-listbox-callback))
  (setq *lb3* (tk-make-listbox () relief: "sunken"
			       yscrollcommand: Fun-listbox-callback))
  (setq *lb4* (tk-make-listbox () relief: "sunken"
			       yscrollcommand: Fun-listbox-callback))

  (tk-pack *lb1* *lb2* *lb3* *lb4* side: "left")
  (tk-pack *scroll* side: "right" fill: "y")

  (while (< i 20)
    (tk-insert *lb1* "end" i)
    (tk-insert *lb2* "end" i)
    (tk-insert *lb3* "end" i)
    (tk-insert *lb4* "end" i)
    (setq i (+ i 1)))

  (Tk_MainLoop))

(prove)
)

This example is available in the module scrolls-example.em in Examples section.

How to manage callbacks.

There are different ways where callback functions are required. Tcl/Tk is a package that works over X system. That means that almost everything is event-driven.

Callback is a procedure whose address is store in advance, and later, at some significant time, the address is used to invoke the procedure. The idea is: set this widget to invoke this function (with this address) when this event occurs. So that, when the event occurs, the procedure is invoked (call it back).

Tcl/Tk supplies this callback mechanism. youtoo/Tk is also able to manage callback procedures.

There are two differents situations where callback functions can be set:

  1. Using the command: option in the creation or configuration of the widgets that support this option.
  2. Using the bind command. This could be done using the general command (tk-bind) or the specific ones (tk-bind-item-canvas, tk-bind-tag-text).

1. Using command: option

There are several widgets that support this option. We are going to use the button widget for the examples, but everything works with all the widgets supporting this option. It works for creation and configuration operation.

Callback without arguments

This is the case when the widget will call a function, without giving any parameters.
This is the simplest case:

  (defun button-callback ()
    (format t "Hello World\n")
    (flush))


  (tk-make-button () text: "Hello World" command: button-callback)
Callback with several arguments

However, not everything is that easy. Most of the times the callback function requires arguments.
To give back arguments to the callback the args: accessor is needed. It can be seen as another pair option value.
The option is args: and the value should be a list with all of the arguments that the callback expect to receive.
The only restriction is that the pair args: list has to be after the pair command: function, otherwise
an error will be reported.

An example of that could be:

  (defun button-callback (name surname)
    (format t "Hello ~s ~s\n" name surname)
    (flush))

  (tk-make-button () text: "Hello" command: button-callback args: (list "Xec" "Xarop"))
Another possibility is to give back, as an argument, the same widget that is beig created or configurated.
The widget is represented by the accessor self: in the list of arguments.
So that:

  (defun button-callback (but name)
    (tk-conf-button but text: name))


  (tk-make-button () text: "Hello World"
                     command: button-callback
                     args: (list self: "Xec"))

2. Using bind command.

Up to date there are three functions to bind widgets or elements of widgets to a callbacks. These the commands are the next ones:

  1. tk-bind
  2. tk-bind-item-canvas
  3. tk-bind-tag-text
Callback without arguments

This is the case when the callback function do not need any arguments.
From this point forward tk-bind, tk-bind-item-canvas and tk-bind-tag-text commands are used indifferently for the examples, but everything works in the same way for all of them. This is the simplest case:

  (defun enter-label-callback ()
    (format t "Hello World\n")
    (flush))

  (setq label (tk-make-label () text: "Hello World" )
  (tk-bind label "" enter-label-callback)
Callback with several arguments

Nevertheless, arguments back to the callback will be required most of the times.
And the user also needs to know some information about the produced event.
So that, if the user wants to use the position of the mouse in a Button click, the binding need to have the next syntax:

  (tk-bind-item-canvas item "<Button-1>" draw-point x: y:)
where x: y: represent the same as "%x" "%y" in Tcl/Tk world.

The function draw-point will receive a couple of arguments (x and y position of the mouse).

There is also the possibility of giving back arguments with the accessor args:. Following this accessor a list with the arguments has to be given. If the user want to use this accessor and the previous ones (information of the event), they have to appear in order. That is, first the accessors to event information and then args: accessor with the list of arguments.

Let's see a couple of examples to illustrate that:

  1. Only event information is given back.
       (defun draw-point (it)
       ...
       )
    
       (tk-bind-item-canvas item "<Button-1>" draw-point args: (list item))
    
  2. Other arguments.
       (defun draw-point (x y it)
       ...
       )
    
       (tk-bind-item-canvas item "<Button-1>" draw-point x: y: args: (list item))
    

How to manage Menus.

Menus are a common tool when the user wants to build an interface. The next example will show how the creation and configuration of menus should be done in youtoo/Tk.

youtoo/Tk system does not allow to construct pull-down menus in the way that Tcl does. The menu widget has to be descendent of the menubutton, but menubutton will not be able to be constructed before the creation of the menu.

The suggestion to solve this problem is given in the following list of steps:

  1. Menubutton creation. E.g.
        (setq text (tk-make-menubutton ()))
    
  2. Menu creation and configuration as a descendent of the button created in step 1. E.g.
        (setq menu-text (tk-make-menu text))
        (tk-menu-add menu-text 'radiobutton ...)
        (tk-menu-add menu-text 'command ...)
    
  3. Menubutton configuration. E.g.
        (tk-conf-widget text text: "Text" underline: "0" menu: menu-text)
    

A complete version with descendent menus is available in the module tk_test_menu.em in Examples section.

Example:

(defun test-menu ()
  (let*
    ((mbar (tk-make-frame () relief: "raised" bd: "2"))
     (dummy (tk-make-frame () width: "10c" height: "5c"))
     (file (tk-make-menubutton mbar))
     (edit (tk-make-menubutton mbar))
     (help (tk-make-menubutton mbar))

     (menu-file (tk-make-menu file tearoff: "0"))
     (menu-edit (tk-make-menu edit tearoff: "0"))
     (menu-help (tk-make-menu help tearoff: "0"))

    (tk-pack mbar dummy side: "top" fill: "x")

    ;;  Because is necessary that the menus were descendents of the menubutton
    ;;  we have to put on the widgets.

    (tk-conf-widget file text: "File" underline: "0" menu: menu-file)
    (tk-conf-widget edit text: "Edit" underline: "0" menu: menu-edit)
    (tk-conf-widget help text: "Help" underline: "0" menu: menu-help)

    ;; Creation of the File menu.

    (tk-menu-add menu-file 'command label: "Exit" command: tk-exit)

    (tk-pack file edit side: "left")
    (tk-pack help side: "right"))
  (Tk_MainLoop))
)