let menu ?(max_per_page=15) ?(always_there=[]) ~sentence items =
let items_splitted =
let split_number = max_per_page - (List.length always_there) in
let rec split acc l =
let left, right = List.split_n l split_number in
match right with
| [] -> List.rev (left :: acc)
| more -> split (left :: acc) more
in
split [] items
in
let number_of_menus = List.length items_splitted in
let rec menu_loop nth =
let items =
always_there @ (List.nth items_splitted nth |> Option.value ~default:[])
in
let available_chars =
List.filter interaction_chars (fun c ->
List.for_all items (fun (k, _, _) -> k <> Some c)) in
let filled_items =
let n = ref 0 in
List.map items ~f:(function
| None, l, v -> (incr n; List.nth available_chars !n, l, v)
| Some k, l, v -> Some k, l, v)
in
let can_previous, can_next =
match number_of_menus, nth with
| 1, 0 -> false, false
| _, 0 -> false, true
| n, t when t = n - 1 -> true, false
| _, _ -> true, true
in
Log.(sentence % sp
% (if nth = 0 && number_of_menus = 1
then empty
else brakets (i (nth + 1) % s "/" % i number_of_menus)) % n
% s (get_key_question ()) % n
% concat
(List.map filled_items ~f:(function
| (Some k, l, v) -> sf "* [%c]: " k % l % n
| None, _, _ -> s "ERROR, wrong usage of `menu`"))
% (if can_previous then sf "* [<]: previous screen" % n else empty)
% (if can_next then sf "* [>]: next screen" % n else empty)
@ normal);
get_key ()
>>= fun key ->
pressed key;
begin match key with
| '<' when can_previous -> menu_loop (nth - 1)
| '>' when can_next -> menu_loop (nth + 1)
| other ->
begin match List.find filled_items (fun (k, _, _) -> k = Some key) with
| Some (_, _, v) -> return v
| None ->
Log.(sf "Cannot understand: %c, try again please." key @ normal);
menu_loop nth
end
end
in
menu_loop 0