The graphical interface materializes the set of keys (digits and functions) and an area for displaying results. Keys can be activated using the graphical interface (and the mouse) or by typing on the keyboard. Figure 5.9 shows the interface we are about to construct.

We reuse the functions for drawing boxes as described on page ??. We define the following type:

Figure 5.9: Graphical calculator.

It contains the state of the calculator, the list of boxes corresponding to the keys and the visualization box. We plan to construct a calculator that is easily modifiable. Therefore, we parameterize the construction of the interface with an association list:

#type`calc_state`

`=`

`{`

`s`

`:`

`state;`

`k`

`:`

(box_config

`*`

`key`

`*`

`string`

)`list;`

`v`

`:`

`box_config`

`}`

`;;`

#let`descr_calc`

`=`

`[`

(Digit

`0`

`,`

`"0"`

);

(Digit

`1`

`,`

`"1"`

);

(Digit

`2`

`,`

`"2"`

);

(Equals`,`

`"="`

);

(Digit

`3`

`,`

`"3"`

);

(Digit

`4`

`,`

`"4"`

);

(Digit

`5`

`,`

`"5"`

);

(Plus`,`

`"+"`

);

(Digit

`6`

`,`

`"6"`

);

(Digit

`7`

`,`

`"7"`

);

(Digit

`8`

`,`

`"8"`

);

(Minus`,`

`"-"`

);

(Digit

`9`

`,`

`"9"`

);

(Recall`,`

`"RCL"`

);

(Div`,`

`"/"`

);

(Times`,`

`"*"`

);

(Off`,`

`"AC"`

);

(Store`,`

`"STO"`

);

(Clear`,`

`"CE/C"`

)

`]`

`;;`

#let`gen_xy`

`vals`

`comp`

`o`

`=`

`List.fold_left`

(fun

`a`

(x`,`

y)`->`

`comp`

(fst`a`

)`x`

`,`

comp

(snd`a`

)`y`

)`o`

`vals`

`;;`

`val gen_xy : ('a * 'a) list -> ('b -> 'a -> 'b) -> 'b * 'b -> 'b * 'b = <fun>`

#let`max_xy`

`vals`

`=`

`gen_xy`

`vals`

`max`

(min_int`,`

min_int);;`val max_xy : (int * int) list -> int * int = <fun>`

#let`max_boxl`

`l`

`=`

let`bmax`

(mx`,`

my)`b`

`=`

`max`

`mx`

`b`

`.`

x`,`

`max`

`my`

`b`

`.`

y

in`List.fold_left`

`bmax`

(min_int`,`

min_int)`l`

`;;`

`val max_boxl : box_config list -> int * int = <fun>`

Here is the principal function

#let`gen_boxes`

`descr`

`n`

`wsep`

`wsepint`

`wbord`

`=`

let`l_l`

`=`

`List.length`

`descr`

in

let`nb_lig`

`=`

if`l_l`

mod`n`

`=`

`0`

then`l_l`

`/`

`n`

else`l_l`

`/`

`n`

`+`

`1`

in

let`ls`

`=`

`List.map`

(fun

(x`,`

y)`->`

`Graphics.text_size`

`y`

)`descr`

in

let`sx`

`,`

sy

`=`

`max_xy`

`ls`

in

let`sx`

`,`

sy`=`

`sx`

`+`

wsepint

`,`

sy`+`

wsepint

in

let`r`

`=`

`ref`

`[]`

in

for`i`

`=`

`0`

to`l_l`

`-`

`1`

do

let`px`

`=`

`i`

mod`n`

and`py`

`=`

`i`

`/`

`n`

in

let`b`

`=`

`{`

`x`

`=`

`wsep`

`*`

(px`+`

`1`

)

`+`

(sx`+`

`2`

`*`

wbord)

`*`

`px`

`;`

`y`

`=`

`wsep`

`*`

(py`+`

`1`

)

`+`

(sy`+`

`2`

`*`

wbord)

`*`

`py`

`;`

`w`

`=`

`sx;`

`h`

`=`

`sy`

`;`

`bw`

`=`

`wbord;`

`r`

`=`

Top;

`b1_col`

`=`

`gray1;`

`b2_col`

`=`

`gray3;`

`b_col`

`=`

gray2}

in`r`

`:=`

`b`

`::!`

r

done;

let`mpx`

`,`

mpy

`=`

`max_boxl`

`!`

r

in

let`upx`

`,`

upy

`=`

`mpx`

`+`

sx`+`

wbord`+`

wsep`,`

mpy`+`

sy`+`

wbord`+`

wsep

in

let

(wa`,`

ha)

`=`

`Graphics.text_size`

`" 0"`

in

let`v`

`=`

`{`

`x`

`=`

(upx`-`

(wa`+`

wsepint

`+`

wbord))`/`

`2`

`;`

`y`

`=`

`upy`

`+`

`wsep;`

`w`

`=`

wa`+`

wsepint;`h`

`=`

`ha`

`+`

wsepint;`bw`

`=`

`wbord`

`*`

`2`

;`r`

`=`

Flat`;`

`b1_col`

`=`

`gray1;`

`b2_col`

`=`

`gray3;`

`b_col`

`=`

Graphics.black}

in

`upx`

`,`

(upy`+`

wsep`+`

ha`+`

wsepint`+`

wsep`+`

`2`

`*`

wbord)`,`

v`,`

`List.map2`

(fun`b`

(x`,`

y)`->`

`b`

`,`

x`,`

y

)

(List.rev

`!`

r)`descr;;`

`val gen_boxes :`

`('a * string) list ->`

`int ->`

`int ->`

`int -> int -> int * int * box_config * (box_config * 'a * string) list =`

`<fun>`

#let`f_key`

`cs`

`c`

`=`

`transition`

`cs`

`.`

s

(translation`c`

);

`erase_box`

`cs`

`.`

v;

`draw_string_in_box`

`Right`

(string_of_int`cs`

`.`

s`.`

vpr)`cs`

`.`

v`Graphics.white`

`;;`

`val f_key : calc_state -> char -> unit = <fun>`

The control of the mouse is a bit more complex. It requires verification that the position of the mouse click is actually in one of the key boxes. For this we first define the auxiliary function

#let`mem`

(x`,`

y)

(x0`,`

y0`,`

w`,`

h)

`=`

(x

`>=`

`x0`

)

`&&`

(x`<`

`x0`

`+`

w)

`&&`

(y`>=`

y0)

`&&`

(`y`

`<`

y0`+`

h);;`val mem : int * int -> int * int * int * int -> bool = <fun>`

#let`f_mouse`

`cs`

`x`

`y`

`=`

try

let`b`

`,`

t`,`

s

`=`

`List.find`

(fun

(b`,_,_`

)`->`

`mem`

(x`,`

y)

(b`.`

x`+`

b`.`

bw`,`

b`.`

y`+`

b`.`

bw`,`

b`.`

w`,`

b`.`

h))`cs`

`.`

k

in

`transition`

`cs`

`.`

s`t;`

`erase_box`

`cs`

`.`

v;

`draw_string_in_box`

`Right`

(string_of_int`cs`

`.`

s`.`

vpr

)`cs`

`.`

v`Graphics.white`

with`Not_found`

`->`

`();;`

`val f_mouse : calc_state -> int -> int -> unit = <fun>`

The function

The function

#let`f_exc`

`cs`

`ex`

`=`

match`ex`

with

`Division_by_zero`

`->`

`transition`

`cs`

`.`

s`Clear;`

`erase_box`

`cs`

`.`

v;

`draw_string_in_box`

`Right`

`"Div 0"`

`cs`

`.`

v

(Graphics.red)

`|`

`Invalid_key`

`->`

`()`

`|`

`Key_off`

`->`

`raise`

`End`

`|`

`_`

`->`

`raise`

`ex;;`

`val f_exc : calc_state -> exn -> unit = <fun>`

In the case of a division by zero, it restarts in the initial state of the calculator and displays an error message on its screen. Invalid keys are simply ignored. Finally, the exception

#let`create_e`

`k`

`=`

`Graphics.close_graph`

`();`

`Graphics.open_graph`

`" 10x10"`

;

let`mx`

`,`

my`,`

v`,`

lb

`=`

`gen_boxes`

`k`

`4`

`4`

`5`

`2`

in

let`s`

`=`

`{lcd`

`=`

`0`

;`lka`

`=`

false;`loa`

`=`

`Equals;`

`vpr`

`=`

`0`

;`mem`

`=`

`0`

}

in

`mx`

`,`

my`,{`

s`=`

s;`k`

`=`

lb;v`=`

v};;`val create_e : (key * string) list -> int * int * calc_state = <fun>`

The initialization function makes use of the result of the preceding function.

#let`f_init`

`mx`

`my`

`cs`

`()`

`=`

`Graphics.close_graph();`

`Graphics.open_graph`

(`" "`

`^`

(string_of_int`mx`

)`^`

`"x"`

`^`

(string_of_int`my`

));

`Graphics.set_color`

`gray2;`

`Graphics.fill_rect`

`0`

`0`

(mx`+`

`1`

)

(my`+`

`1`

);

`List.iter`

(fun

(b`,_,_`

)`->`

`draw_box`

`b`

)`cs`

`.`

k;

`List.iter`

(fun

(b`,_,`

s)`->`

`draw_string_in_box`

`Center`

`s`

`b`

`Graphics.black`

)`cs`

`.`

k`;`

`draw_box`

`cs`

`.`

v;

`erase_box`

`cs`

`.`

v;

`draw_string_in_box`

`Right`

`"hello"`

`cs`

`.`

v

(Graphics.white);;`val f_init : int -> int -> calc_state -> unit -> unit = <fun>`

Finally the termination function closes the graphical window.

#let`f_end`

`e`

`()`

`=`

`Graphics.close_graph();;`

`val f_end : 'a -> unit -> unit = <fun>`

The function

#let`go`

`descr`

`=`

let`mx`

`,`

my`,`

e

`=`

`create_e`

`descr`

in

`skel`

(f_init`mx`

`my`

`e`

)

(f_end`e`

)

(f_key`e`

)

(f_mouse`e`

)

(f_exc`e`

);;`val go : (key * string) list -> unit = <fun>`

The call to