## 5.12 Example: Help Popups

In the following we want to look at a small example which provides for a generic interactive help popup window. The idea is that if the mouse pointer stays over a widget for some time without pressing a mouse button, a small help text is displayed. The help text should disappear if the mouse pointer leaves the screen area covered by the widget.

We will build a procedure `AttachHelp` such that help texts are enabled by application of the procedure to a widget and a help text. We proceed in three steps, the first is to create a function to create a toplevel widget that displays the help text, the second is a listener class (that is, a subclass of `Tk.listener`), and the last step is the definition of `AttachHelp` itself.

### 5.12.1 Displaying Help

The procedure `MakePopup` shown in Figure 5.12 takes a widget and the help text as its argument and returns a function to create a toplevel widget containing the text at a position relative to the widget on the screen.

<Definition of MakePopup>=
  `fun {MakePopup Parent Text}
  fun {$}
  [X Y H]={Map [rootx rooty height]
  fun {$ WI}
  {Tk.returnInt winfo(WI Parent)}
  end}
  W={New Tk.toplevel tkInit(withdraw:true bg:black)}
  M={New Tk.message
  tkInit(parent:W text:Text bg:khaki aspect:400)}
  in
  {Tk.batch [wm(overrideredirect W true)
  wm(geometry W '+'#X+10#'+'#Y+H)
  pack(M padx:2 pady:2)
  wm(deiconify W)]}
  W
  end
  end`

Figure 5.12: Creating a help window.

The returned function creates a toplevel widget in withdrawn state and configures the toplevel widget such that it:

1. is not equipped with a frame from the window manager. This is done by using the window manager command `overrideredirect`: the window manager is advised to not ``redirect'' the toplevel widget into a frame.

2. appears at a position relative to `X` and `Y` coordinates of the widget parent, which done by the `geometry` window manager command.

3. appears on the screen by deiconifying it.

### 5.12.2 The Listener Class

The listener class `HelpListener` is shown in Figure 5.13. The method `init` initializes an instance of this class by creating a procedure for creation of the popup widget.

<Definition of HelpListener>=
  `class HelpListener from Tk.listener
  attr
  cancel: unit
  popup: unit
  meth init(parent:P text:T)
  popup := {MakePopup P T}
  Tk.listener,tkInit
  end
  meth enter
  C
  in
  cancel := C
  thread A={Alarm 1000} in
  {WaitOr C A}
  if {IsDet A} then W={@popup} in
  {Wait C} {W tkClose}
  end
  end
  end
  meth leave
  @cancel=unit
  end
  end`

Figure 5.13: The listener class `HelpListener`.

When the mouse pointer enters the parent widget, the method `enter` gets executed. This method stores a new variable `C` in the attribute `cancel` which serves as flag whether the help popup must be closed. The newly created thread waits until either one second has elapsed (`A` gets bound after 1000 milliseconds) or the widget has been left (`C` gets bound). Then possibly the widget for the help text is created, which gets closed when the parent widget is left. Note that if both `A` and `C` happen to be bound at the same time, the popup will be created and then closed immediately.

The `leave` method signals that the help popup must be closed by binding the variable stored in `cancel`.

### 5.12.3 `AttachHelp`

The definition of `AttachHelp` is shown in Figure 5.14. It creates a listener and creates event bindings that are served by that listener.

  `local
  ` <Definition of MakePopup>`
  ` <Definition of HelpListener>`
  in
  proc {AttachHelp Widget Text}
  L={New HelpListener init(parent:Widget text:Text)}
  in
  {Widget tkBind(event:'<Enter>' action:L#enter append:true)}
  {Widget tkBind(event:'<Leave>' action:L#leave append:true)}
  {Widget tkBind(event:'<Button>' action:L#leave append:true)}
  end
  end`

Figure 5.14.

### 5.12.4 Using Help Popups

A small example that shows how to use help popups is shown in Figure 5.15.

  `Bs={Map ['Okay' # 'Do nothing meaningful.'
  'Cancel' # 'Do nothing at all.'
  'Quit' # 'Close the window.']
  fun {$ Text # Help}
  B={New Tk.button tkInit(parent:W text:Text)}
  in
  {AttachHelp B Help} B
  end}
  {Tk.send pack(b(Bs) side:left padx:2#m pady:2#m)}`

Figure 5.15: Demo of the `HelpPopup` class.