package miaou-core

  1. Overview
  2. Docs
Miaou core/widgets (no drivers, no SDL)

Install

dune-project
 Dependency

Authors

Maintainers

Sources

v0.5.2.tar.gz
md5=60a3b9f181f24572a06a9492532bfdda
sha512=fcc35a275066be2900e6201782faf47503076fa4640f08cf78067835a6f447b74613009e55b2ac799adb7ca46f1bffa261fc5971753f2cc3c6bef327511c7ef6

doc/src/miaou_widgets_navigation/tabs_widget.ml.html

Source file tabs_widget.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
(*****************************************************************************)
(*                                                                           *)
(* SPDX-License-Identifier: MIT                                              *)
(* Copyright (c) 2025 Nomadic Labs <contact@nomadic-labs.com>                *)
(*                                                                           *)
(*****************************************************************************)

[@@@warning "-32-34-37-69"]

module W = Miaou_widgets_display.Widgets
module Helpers = Miaou_helpers.Helpers

type tab = {id : string; label : string}

type t = {tabs : tab list; selected : int}

let clamp idx len = if len = 0 then 0 else max 0 (min (len - 1) idx)

let tab ~id ~label = {id; label}

let id t = t.id

let label t = t.label

let make tabs =
  let tabs =
    List.filter (fun t -> String.length t.label > 0) tabs
    |> List.map (fun t -> {id = t.id; label = t.label})
  in
  {tabs; selected = clamp 0 (List.length tabs)}

let current t =
  match List.nth_opt t.tabs t.selected with Some x -> Some x | None -> None

let move t dir =
  let len = List.length t.tabs in
  if len = 0 then t
  else
    let next =
      match dir with
      | `Left -> (t.selected - 1 + len) mod len
      | `Right -> (t.selected + 1) mod len
      | `First -> 0
      | `Last -> len - 1
    in
    {t with selected = next}

let select t ~id =
  let rec find i = function
    | [] -> t.selected
    | x :: xs -> if String.equal x.id id then i else find (i + 1) xs
  in
  let len = List.length t.tabs in
  let idx = clamp (find 0 t.tabs) len in
  {t with selected = idx}

let handle_event ?(bubble_unhandled = false) t ~key =
  match Miaou_core.Keys.of_string key with
  | Some Miaou_core.Keys.Left -> (move t `Left, `Handled)
  | Some Miaou_core.Keys.Right -> (move t `Right, `Handled)
  | Some Miaou_core.Keys.Home -> (move t `First, `Handled)
  | Some Miaou_core.Keys.End -> (move t `Last, `Handled)
  | _ -> (
      (* Check for mouse click to select tab *)
      match Miaou_helpers.Mouse.parse_click key with
      | Some {col; _} -> (
          (* Calculate which tab was clicked based on column position.
             Each tab is: " label " (padded) + "|" separator.
             Separator is 1 visible char. *)
          let rec find_tab idx pos tabs =
            match tabs with
            | [] -> None
            | tab :: rest ->
                let tab_width = String.length tab.label + 2 in
                (* " label " *)
                let sep_width = if rest = [] then 0 else 1 in
                (* "|" *)
                let total = tab_width + sep_width in
                if col >= pos && col < pos + tab_width then Some idx
                else find_tab (idx + 1) (pos + total) rest
          in
          match find_tab 0 1 t.tabs with
          | Some idx -> ({t with selected = idx}, `Handled)
          | None -> (t, if bubble_unhandled then `Bubble else `Handled))
      | None -> (t, if bubble_unhandled then `Bubble else `Handled))

let handle_key t ~key =
  let t, _ = handle_event t ~key in
  t

let render t ~focus =
  let pad s = if String.length s = 0 then s else Printf.sprintf " %s " s in
  let highlight s = if focus then W.bold s else s in
  let rendered =
    List.mapi
      (fun i tab ->
        if i = t.selected then highlight (pad tab.label)
        else W.dim (pad tab.label))
      t.tabs
  in
  match rendered with
  | [] -> ""
  | _ -> Helpers.concat_with_sep (W.dim "|") rendered