Source file Flex_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
type item = {
base : float;
grow : float;
shrink : float;
min_size : float;
max_size : float;
auto_before : bool;
auto_after : bool;
}
let clamp (it : item) (size : float) : float = Float.max it.min_size (Float.min it.max_size size)
let lines ~(wrap : bool) ~(room : float) ~(gap : float) (items : item array) : (int * int) list =
let n = Array.length items in
if n = 0 then []
else if not wrap then [ (0, n - 1) ]
else
let rec go start acc =
if start >= n then List.rev acc
else
let rec extend j used =
if j + 1 < n && used +. gap +. clamp items.(j + 1) items.(j + 1).base <= room then extend (j + 1) (used +. gap +. clamp items.(j + 1) items.(j + 1).base)
else j
in
let j = extend start (clamp items.(start) items.(start).base) in
go (j + 1) ((start, j) :: acc)
in
go 0 []
let resolve ~(room : float) ~(gap : float) (items : item array) : float array =
let n = Array.length items in
let gaps = gap *. float_of_int (max 0 (n - 1)) in
let growing = room -. gaps -. Array.fold_left (fun s it -> s +. clamp it it.base) 0. items >= 0. in
let frozen = Array.map (fun it -> if growing then it.grow = 0. else it.shrink = 0.) items in
let sizes = Array.map (fun it -> clamp it it.base) items in
let rec loop () =
if Array.exists not frozen then (
let free = ref (room -. gaps) and grow = ref 0. and scaled = ref 0. in
Array.iteri
(fun i it ->
if frozen.(i) then free := !free -. sizes.(i)
else (
free := !free -. it.base;
grow := !grow +. it.grow;
scaled := !scaled +. (it.shrink *. it.base)))
items;
let free = if growing && !grow < 1. then !free *. !grow else !free in
let target =
Array.mapi
(fun i it ->
if frozen.(i) then sizes.(i)
else if growing then if !grow > 0. then it.base +. (free *. it.grow /. !grow) else it.base
else if !scaled > 0. then it.base +. (free *. it.shrink *. it.base /. !scaled)
else it.base)
items
in
let total = ref 0. in
let clamped = Array.mapi (fun i it -> if frozen.(i) then sizes.(i) else clamp it target.(i)) items in
Array.iteri (fun i _ -> if not frozen.(i) then total := !total +. (clamped.(i) -. target.(i))) items;
if Float.abs !total < 1e-9 then Array.iteri (fun i _ -> if not frozen.(i) then (sizes.(i) <- clamped.(i); frozen.(i) <- true)) items
else (
Array.iteri
(fun i _ ->
let v = clamped.(i) -. target.(i) in
if (not frozen.(i)) && ((!total > 0. && v > 0.) || (!total < 0. && v < 0.)) then (
sizes.(i) <- clamped.(i);
frozen.(i) <- true))
items;
loop ()))
in
loop ();
sizes
let place ~(justify : Computed.align) ~(room : float) ~(gap : float) (items : item array) (sizes : float array) : float array =
let n = Array.length items in
let used = Array.fold_left ( +. ) 0. sizes +. (gap *. float_of_int (max 0 (n - 1))) in
let left = Float.max 0. (room -. used) in
let autos = Array.fold_left (fun k it -> k + (if it.auto_before then 1 else 0) + if it.auto_after then 1 else 0) 0 items in
let first, between =
if autos > 0 then (0., 0.)
else
match justify with
| End -> (left, 0.)
| Center -> (left /. 2., 0.)
| Space_between -> (0., if n > 1 then left /. float_of_int (n - 1) else 0.)
| Space_around -> (left /. float_of_int (2 * n), left /. float_of_int n)
| Space_evenly -> (left /. float_of_int (n + 1), left /. float_of_int (n + 1))
| Start | Stretch | Align_baseline -> (0., 0.)
in
let per_auto = if autos > 0 then left /. float_of_int autos else 0. in
let pos = ref first in
Array.mapi
(fun i it ->
if it.auto_before then pos := !pos +. per_auto;
let at = !pos in
pos := !pos +. sizes.(i) +. gap +. between +. if it.auto_after then per_auto else 0.;
at)
items
let cross ~(align : Computed.align) ~(line : float) ~(size : float) : float * float =
match align with
| End -> (line -. size, size)
| Center -> ((line -. size) /. 2., size)
| Stretch -> (0., Float.max size line)
| Start | Align_baseline | Space_between | Space_around | Space_evenly -> (0., size)