package tiny_libs
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
From-scratch libraries for teaching: graphics, audio, compression, crypto, networking and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/tiny_libs.gui/Layout.ml.html
Source file Layout.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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189(* Claude Code * * Copyright (C) 2026 Yoann Padioleau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * (LGPL) as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *) (* See Layout.mli *) (*****************************************************************************) (* Types *) (*****************************************************************************) type constraints = { min_w : float; max_w : float; min_h : float; max_h : float } let loose w h = { min_w = 0.; max_w = w; min_h = 0.; max_h = h } let tight w h = { min_w = w; max_w = w; min_h = h; max_h = h } type 'a t = | Leaf of 'a * (float * float) | Space of float | Spacer | Expand of 'a t | Stretch of 'a t | Pad of float * 'a t | Center of 'a t | Row of float * 'a t list | Column of float * 'a t list let leaf key size = Leaf (key, size) let space n = Space n let spacer = Spacer let expand t = Expand t let stretch t = Stretch t let pad n t = Pad (n, t) let center t = Center t let row ?(gap = 0.) kids = Row (gap, kids) let column ?(gap = 0.) kids = Column (gap, kids) (*****************************************************************************) (* Helpers *) (*****************************************************************************) (* A column is a row turned on its side, so everything below is * written once, along an axis: [main] is the direction it runs in, * [cross] the other one. *) type axis = Horizontal | Vertical let main axis (w, h) = match axis with Horizontal -> w | Vertical -> h let cross axis (w, h) = match axis with Horizontal -> h | Vertical -> w let of_axis axis ~main ~cross = match axis with Horizontal -> (main, cross) | Vertical -> (cross, main) let clamp c (w, h) = (max c.min_w (min c.max_w w), max c.min_h (min c.max_h h)) (* the two flags look through each other: [expand (stretch x)] and * [stretch (expand x)] are both a child that fills the room along the * axis *and* across it -- a pane in a window, which is the commonest * thing a layout holds *) let rec is_flexible = function Spacer | Expand _ -> true | Stretch t -> is_flexible t | _ -> false let rec is_stretch = function Stretch _ -> true | Expand t -> is_stretch t | _ -> false (* [share] along the axis, whatever it is given across it *) let along axis c = match axis with | Horizontal -> { c with min_w = share; max_w = share; min_h = 0. } | Vertical -> { c with min_h = share; max_h = share; min_w = 0. } (*****************************************************************************) (* Pass one: constraints down, sizes up *) (*****************************************************************************) (* Pass one. The flex rule, which is the whole of rows and columns: * measure the children that know their size, then share what is left * over between the flexible ones. *) let rec child_sizes axis c gap kids = let room = main axis (c.max_w, c.max_h) in let gaps = gap *. float_of_int (max 0 (List.length kids - 1)) in let loose_c = { c with min_w = 0.; min_h = 0. } in let known = kids |> List.map (fun k -> if is_flexible k then None else match k with | Space n -> Some (of_axis axis ~main:n ~cross:0.) | k -> Some (measure loose_c k)) in let used = List.fold_left (fun acc -> function Some s -> acc +. main axis s | None -> acc) 0. known in let flexible = List.length (List.filter is_flexible kids) in let = if flexible = 0 then 0. else max 0. (room -. gaps -. used) /. float_of_int flexible in List.map2 (fun k known -> match (known, k) with | Some s, _ -> s | None, Spacer -> of_axis axis ~main:share ~cross:0. (* an expanded child is measured again, now that it knows how * much it got: constraints down, once more *) | None, k -> measure (along axis share c) k) kids known and measure_line axis c gap kids = let sizes = child_sizes axis c gap kids in let gaps = gap *. float_of_int (max 0 (List.length kids - 1)) in let extent = List.fold_left (fun acc s -> acc +. main axis s) gaps sizes in let thickness = List.fold_left (fun acc s -> max acc (cross axis s)) 0. sizes in (* a line with a flexible child takes all the room it was offered: * that is what makes [spacer] push things apart *) let extent = if List.exists is_flexible kids then main axis (c.max_w, c.max_h) else extent in clamp c (of_axis axis ~main:extent ~cross:thickness) and measure c t = match t with | Leaf (_, size) -> clamp c size (* both mean something only inside a row or a column, which handles * them itself (child_sizes): alone, they are nothing *) | Space _ | Spacer -> clamp c (0., 0.) | Expand inner | Stretch inner -> measure c inner | Pad (n, inner) -> let inner_c = { min_w = max 0. (c.min_w -. (2. *. n)); max_w = max 0. (c.max_w -. (2. *. n)); min_h = max 0. (c.min_h -. (2. *. n)); max_h = max 0. (c.max_h -. (2. *. n)); } in let w, h = measure inner_c inner in clamp c (w +. (2. *. n), h +. (2. *. n)) | Center inner -> measure c inner | Row (gap, kids) -> measure_line Horizontal c gap kids | Column (gap, kids) -> measure_line Vertical c gap kids (*****************************************************************************) (* Pass two: the parent positions *) (*****************************************************************************) (* Pass two: each parent hands its children the rectangle it decided * for them, and only the leaves come back. *) let rec arrange (b : Widget.box) t = match t with | Leaf (key, _) -> [ (key, b) ] | Space _ | Spacer -> [] | Expand inner | Stretch inner -> arrange b inner | Pad (n, inner) -> arrange (Widget.inset n b) inner | Center inner -> let w, h = measure (loose b.w b.h) inner in arrange { b with w; h } inner | Row (gap, kids) -> arrange_line Horizontal b gap kids | Column (gap, kids) -> arrange_line Vertical b gap kids and arrange_line axis (b : Widget.box) gap kids = let sizes = child_sizes axis (loose b.w b.h) gap kids in (* a row walks right from its left edge, a column *down* from its * top: y is up in the playground, so a column's steps are negative *) let start, step = match axis with | Horizontal -> (Widget.left b, 1.) | Vertical -> (Widget.top b, -1.) in let _, places = List.fold_left2 (fun (pos, acc) k size -> let extent = main axis size in (* across the axis, a child keeps the size it asked for and is * centered -- unless it was stretched, and then it fills *) let thickness = if is_stretch k then cross axis (b.w, b.h) else cross axis size in let center_of_child = pos +. (step *. extent /. 2.) in let w, h = of_axis axis ~main:extent ~cross:thickness in let child = match axis with | Horizontal -> { Widget.x = center_of_child; y = b.y; w; h } | Vertical -> { Widget.x = b.x; y = center_of_child; w; h } in (pos +. (step *. (extent +. gap)), acc @ arrange child k)) (start, []) kids sizes in places
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>