-module(etui@geometry). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/etui/geometry.gleam"). -export([rect_new/4, rect_zero/0, right/1, bottom/1, area/1, contains/2, hit_test/3, intersect/2, union/2, resolve_sizes/2, split/3, split_h/2, split_v/2, centered_rect/3, percent_rect/3, split_with_spacing/4, split_flex/5, split_responsive/2]). -export_type([position/0, size/0, rect/0, direction/0, constraint/0, flex_justify/0, breakpoint/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type position() :: {position, integer(), integer()}. -type size() :: {size, integer(), integer()}. -type rect() :: {rect, position(), size()}. -type direction() :: horizontal | vertical. -type constraint() :: {length, integer()} | {min, integer()} | {max, integer()} | {percentage, integer()} | {ratio, integer(), integer()} | fill. -type flex_justify() :: flex_start | flex_end | flex_center | flex_between | flex_around. -type breakpoint() :: {breakpoint, integer(), list(constraint())}. -file("src/etui/geometry.gleam", 53). ?DOC(" Create a Rect with clamped width/height to non-negative.\n"). -spec rect_new(integer(), integer(), integer(), integer()) -> rect(). rect_new(X, Y, Width, Height) -> {rect, {position, X, Y}, {size, gleam@int:max(0, Width), gleam@int:max(0, Height)}}. -file("src/etui/geometry.gleam", 61). ?DOC(" Zero-sized rect at origin.\n"). -spec rect_zero() -> rect(). rect_zero() -> {rect, {position, 0, 0}, {size, 0, 0}}. -file("src/etui/geometry.gleam", 69). ?DOC(" X coordinate of the right edge (exclusive: x + width).\n"). -spec right(rect()) -> integer(). right(Rect) -> erlang:element(2, erlang:element(2, Rect)) + erlang:element( 2, erlang:element(3, Rect) ). -file("src/etui/geometry.gleam", 74). ?DOC(" Y coordinate of the bottom edge (exclusive: y + height).\n"). -spec bottom(rect()) -> integer(). bottom(Rect) -> erlang:element(3, erlang:element(2, Rect)) + erlang:element( 3, erlang:element(3, Rect) ). -file("src/etui/geometry.gleam", 79). ?DOC(" Area in cells.\n"). -spec area(rect()) -> integer(). area(Rect) -> erlang:element(2, erlang:element(3, Rect)) * erlang:element( 3, erlang:element(3, Rect) ). -file("src/etui/geometry.gleam", 84). ?DOC(" Check if a position is inside the rect (inclusive of edges).\n"). -spec contains(rect(), position()) -> boolean(). contains(Rect, Pos) -> (((erlang:element(2, Pos) >= erlang:element(2, erlang:element(2, Rect))) andalso (erlang:element(2, Pos) < right(Rect))) andalso (erlang:element(3, Pos) >= erlang:element( 3, erlang:element(2, Rect) ))) andalso (erlang:element(3, Pos) < bottom(Rect)). -file("src/etui/geometry.gleam", 93). ?DOC( " True if terminal cell `(x, y)` is inside `rect`.\n" " Convenience wrapper over `contains` for use with mouse event coordinates.\n" ). -spec hit_test(rect(), integer(), integer()) -> boolean(). hit_test(Rect, X, Y) -> contains(Rect, {position, X, Y}). -file("src/etui/geometry.gleam", 98). ?DOC(" Intersection of two rects. Returns the overlapping rect if any.\n"). -spec intersect(rect(), rect()) -> {ok, rect()} | {error, nil}. intersect(A, B) -> Left = gleam@int:max( erlang:element(2, erlang:element(2, A)), erlang:element(2, erlang:element(2, B)) ), Top = gleam@int:max( erlang:element(3, erlang:element(2, A)), erlang:element(3, erlang:element(2, B)) ), Right_edge = gleam@int:min(right(A), right(B)), Bottom_edge = gleam@int:min(bottom(A), bottom(B)), case (Left < Right_edge) andalso (Top < Bottom_edge) of true -> {ok, {rect, {position, Left, Top}, {size, Right_edge - Left, Bottom_edge - Top}}}; false -> {error, nil} end. -file("src/etui/geometry.gleam", 115). ?DOC(" Union of two rects: smallest rect that contains both.\n"). -spec union(rect(), rect()) -> rect(). union(A, B) -> Left = gleam@int:min( erlang:element(2, erlang:element(2, A)), erlang:element(2, erlang:element(2, B)) ), Top = gleam@int:min( erlang:element(3, erlang:element(2, A)), erlang:element(3, erlang:element(2, B)) ), Right_edge = gleam@int:max(right(A), right(B)), Bottom_edge = gleam@int:max(bottom(A), bottom(B)), {rect, {position, Left, Top}, {size, Right_edge - Left, Bottom_edge - Top}}. -file("src/etui/geometry.gleam", 395). -spec pick_size( constraint(), list(integer()), list(integer()), list(integer()), list(integer()) ) -> integer(). pick_size(Constraint, Lengths, Pcts, Ratios, Flexes) -> case Constraint of {length, _} -> case Lengths of [H | _] -> H; _ -> 0 end; {percentage, _} -> case Pcts of [H@1 | _] -> H@1; _ -> 0 end; {ratio, _, _} -> case Ratios of [H@2 | _] -> H@2; _ -> 0 end; fill -> case Flexes of [H@3 | _] -> H@3; _ -> 0 end; {min, _} -> case Flexes of [H@3 | _] -> H@3; _ -> 0 end; {max, _} -> case Flexes of [H@3 | _] -> H@3; _ -> 0 end end. -file("src/etui/geometry.gleam", 362). -spec assemble_sizes( list(constraint()), list(integer()), list(integer()), list(integer()), list(integer()), list(integer()) ) -> list(integer()). assemble_sizes( Constraints, Length_sizes, Pct_sizes, Ratio_sizes, Flex_sizes, Acc ) -> case Constraints of [] -> lists:reverse(Acc); [C | Cs] -> Size = pick_size( C, Length_sizes, Pct_sizes, Ratio_sizes, Flex_sizes ), Ls = case Length_sizes of [_ | T] -> T; _ -> [] end, Ps = case Pct_sizes of [_ | T@1] -> T@1; _ -> [] end, Rs = case Ratio_sizes of [_ | T@2] -> T@2; _ -> [] end, Fs = case Flex_sizes of [_ | T@3] -> T@3; _ -> [] end, assemble_sizes(Cs, Ls, Ps, Rs, Fs, [Size | Acc]) end. -file("src/etui/geometry.gleam", 303). -spec phase_flex( list(constraint()), integer(), integer(), integer(), list(integer()) ) -> list(integer()). phase_flex(Constraints, Flex_count, Budget, _, _) -> case Flex_count of 0 -> gleam@list:map(Constraints, fun(_) -> 0 end); _ -> Base = case Flex_count of 0 -> 0; Gleam@denominator -> Budget div Gleam@denominator end, Min_max_used = gleam@list:fold( Constraints, 0, fun(Acc, C) -> case C of {min, N} -> Acc + gleam@int:max(N, Base); {max, N@1} -> Acc + gleam@int:min(N@1, Base); _ -> Acc end end ), Fill_count = gleam@list:count(Constraints, fun(C@1) -> case C@1 of fill -> true; _ -> false end end), Fill_budget = gleam@int:max(0, Budget - Min_max_used), Fill_base = case Fill_count of 0 -> 0; _ -> case Fill_count of 0 -> 0; Gleam@denominator@1 -> Fill_budget div Gleam@denominator@1 end end, Fill_rem = case Fill_count of 0 -> 0; _ -> case Fill_count of 0 -> 0; Gleam@denominator@2 -> Fill_budget rem Gleam@denominator@2 end end, {Sizes, _} = gleam@list:fold( Constraints, {[], 0}, fun(State, C@2) -> {Acc@1, Fill_idx} = State, {Size, New_fill_idx} = case C@2 of fill -> S = case Fill_idx < Fill_rem of true -> Fill_base + 1; false -> Fill_base end, {S, Fill_idx + 1}; {min, N@2} -> {gleam@int:max(N@2, Base), Fill_idx}; {max, N@3} -> {gleam@int:min(N@3, Base), Fill_idx}; _ -> {0, Fill_idx} end, {[Size | Acc@1], New_fill_idx} end ), lists:reverse(Sizes) end. -file("src/etui/geometry.gleam", 269). -spec phase_proportional( list(integer()), integer(), integer(), integer(), integer(), list(integer()) ) -> {list(integer()), integer()}. phase_proportional(Demands, Budget, Total_demand, Cumsum, Prev_target, Acc) -> case Demands of [] -> {lists:reverse(Acc), Prev_target}; [D | Rest] -> New_cumsum = Cumsum + D, Target = case Total_demand of 0 -> 0; _ -> case Total_demand =< Budget of true -> New_cumsum; false -> case Total_demand of 0 -> 0; Gleam@denominator -> Budget * New_cumsum div Gleam@denominator end end end, Size = Target - Prev_target, phase_proportional( Rest, Budget, Total_demand, New_cumsum, Target, [Size | Acc] ) end. -file("src/etui/geometry.gleam", 238). -spec phase_percentage( list(constraint()), integer(), integer(), integer(), integer(), list(integer()) ) -> {list(integer()), integer()}. phase_percentage(Constraints, Denom, Base, Acc_pct, Prev_target, Acc) -> case Constraints of [] -> {lists:reverse(Acc), Prev_target}; [C | Rest] -> {Size, New_acc, New_target} = case C of {percentage, P} -> New_acc_pct = Acc_pct + P, Target = case Denom of 0 -> 0; _ -> case Denom of 0 -> 0; Gleam@denominator -> Base * New_acc_pct div Gleam@denominator end end, S = Target - Prev_target, {S, New_acc_pct, Target}; _ -> {0, Acc_pct, Prev_target} end, phase_percentage( Rest, Denom, Base, New_acc, New_target, [Size | Acc] ) end. -file("src/etui/geometry.gleam", 215). -spec phase_length(list(constraint()), integer(), integer(), list(integer())) -> {list(integer()), integer()}. phase_length(Constraints, Total, Used, Acc) -> case Constraints of [] -> {lists:reverse(Acc), Used}; [C | Rest] -> {Size, New_used} = case C of {length, V} -> Take = gleam@int:min(V, gleam@int:max(0, Total - Used)), {Take, Used + Take}; _ -> {0, Used} end, phase_length(Rest, Total, New_used, [Size | Acc]) end. -file("src/etui/geometry.gleam", 148). -spec resolve_sizes_impl(integer(), list(constraint())) -> list(integer()). resolve_sizes_impl(Total, Constraints) -> {Length_sizes, Length_used} = phase_length(Constraints, Total, 0, []), Prop_budget = gleam@int:max(0, Total - Length_used), Pct_total_pct = gleam@list:fold(Constraints, 0, fun(Acc, C) -> case C of {percentage, P} -> Acc + P; _ -> Acc end end), {Denom, Pct_base} = case (Total * Pct_total_pct) > (Prop_budget * 100) of true -> {Pct_total_pct, Prop_budget}; false -> {100, Total} end, {Pct_sizes, Pct_used} = phase_percentage( Constraints, Denom, Pct_base, 0, 0, [] ), Ratio_budget = gleam@int:max(0, Prop_budget - Pct_used), Ratio_demands = gleam@list:map(Constraints, fun(C@1) -> case C@1 of {ratio, A, B} -> case B of 0 -> 0; _ -> case B of 0 -> 0; Gleam@denominator -> Total * A div Gleam@denominator end end; _ -> 0 end end), Total_ratio_demand = gleam@list:fold( Ratio_demands, 0, fun(Acc@1, D) -> Acc@1 + D end ), {Ratio_sizes, Ratio_used} = phase_proportional( Ratio_demands, Ratio_budget, Total_ratio_demand, 0, 0, [] ), Flex_budget = gleam@int:max( 0, ((Total - Length_used) - Pct_used) - Ratio_used ), Flex_count = gleam@list:count(Constraints, fun(C@2) -> case C@2 of fill -> true; {min, _} -> true; {max, _} -> true; _ -> false end end), Flex_sizes = phase_flex(Constraints, Flex_count, Flex_budget, 0, []), assemble_sizes( Constraints, Length_sizes, Pct_sizes, Ratio_sizes, Flex_sizes, [] ). -file("src/etui/geometry.gleam", 141). ?DOC( " Distribute total space among constraints.\n" "\n" " Returns a list of sizes (one per constraint) that sum to ≤ total.\n" " Sum equals total when Fill (or Min/Max) is present or constraints saturate.\n" "\n" " Algorithm (three-phase Discrete Cumulative Allocation):\n" " 1. Length, exact, allocated first. Clamped to remaining budget in order.\n" " 2. Percentage + Ratio, proportional from total. Cumulative to prevent jitter.\n" " Scaled proportionally if combined demand exceeds available budget.\n" " 3. Fill + Min + Max, divide remaining equally.\n" " Min applies a floor; Max applies a ceiling. Fill gets equal share.\n" ). -spec resolve_sizes(integer(), list(constraint())) -> list(integer()). resolve_sizes(Total, Constraints) -> case Total < 0 of true -> gleam@list:map(Constraints, fun(_) -> 0 end); false -> resolve_sizes_impl(Total, Constraints) end. -file("src/etui/geometry.gleam", 481). -spec build_rects(direction(), rect(), list(integer()), integer(), list(rect())) -> list(rect()). build_rects(Direction, Area, Sizes, Cursor, Acc) -> Limit = case Direction of vertical -> erlang:element(3, erlang:element(3, Area)); horizontal -> erlang:element(2, erlang:element(3, Area)) end, case Sizes of [] -> lists:reverse(Acc); [Size | Rest] -> Start = gleam@int:min(Cursor, Limit), Clamped = gleam@int:min(Size, gleam@int:max(0, Limit - Start)), Rect = case Direction of vertical -> {rect, {position, erlang:element(2, erlang:element(2, Area)), erlang:element(3, erlang:element(2, Area)) + Start}, {size, erlang:element(2, erlang:element(3, Area)), Clamped}}; horizontal -> {rect, {position, erlang:element(2, erlang:element(2, Area)) + Start, erlang:element(3, erlang:element(2, Area))}, {size, Clamped, erlang:element(3, erlang:element(3, Area))}} end, build_rects(Direction, Area, Rest, Start + Clamped, [Rect | Acc]) end. -file("src/etui/geometry.gleam", 466). ?DOC(" Split a rect along a direction by applying constraints.\n"). -spec split(direction(), rect(), list(constraint())) -> list(rect()). split(Direction, Area, Constraints) -> Total = case Direction of vertical -> erlang:element(3, erlang:element(3, Area)); horizontal -> erlang:element(2, erlang:element(3, Area)) end, Sizes = resolve_sizes(Total, Constraints), build_rects(Direction, Area, Sizes, 0, []). -file("src/etui/geometry.gleam", 430). ?DOC(" Split horizontally (columns side-by-side). Shorthand for `split(Horizontal, ...)`.\n"). -spec split_h(rect(), list(constraint())) -> list(rect()). split_h(Area, Constraints) -> split(horizontal, Area, Constraints). -file("src/etui/geometry.gleam", 435). ?DOC(" Split vertically (rows stacked). Shorthand for `split(Vertical, ...)`.\n"). -spec split_v(rect(), list(constraint())) -> list(rect()). split_v(Area, Constraints) -> split(vertical, Area, Constraints). -file("src/etui/geometry.gleam", 445). ?DOC( " Center a rect of `width × height` within `area`.\n" " Clamps to area bounds. Common for popup placement.\n" "\n" " ```gleam\n" " let popup_area = geometry.centered_rect(60, 20, screen)\n" " ```\n" ). -spec centered_rect(integer(), integer(), rect()) -> rect(). centered_rect(Width, Height, Area) -> W = gleam@int:min(Width, erlang:element(2, erlang:element(3, Area))), H = gleam@int:min(Height, erlang:element(3, erlang:element(3, Area))), X = erlang:element(2, erlang:element(2, Area)) + ((erlang:element( 2, erlang:element(3, Area) ) - W) div 2), Y = erlang:element(3, erlang:element(2, Area)) + ((erlang:element( 3, erlang:element(3, Area) ) - H) div 2), {rect, {position, X, Y}, {size, W, H}}. -file("src/etui/geometry.gleam", 459). ?DOC( " Center a rect sized as a percentage of `area` (`pct_w` and `pct_h` are 0–100).\n" " Useful for responsive popup sizing:\n" "\n" " ```gleam\n" " let popup_area = geometry.percent_rect(60, 40, screen) // 60% wide, 40% tall\n" " ```\n" ). -spec percent_rect(integer(), integer(), rect()) -> rect(). percent_rect(Pct_w, Pct_h, Area) -> W = (erlang:element(2, erlang:element(3, Area)) * gleam@int:clamp( Pct_w, 0, 100 )) div 100, H = (erlang:element(3, erlang:element(3, Area)) * gleam@int:clamp( Pct_h, 0, 100 )) div 100, centered_rect(W, H, Area). -file("src/etui/geometry.gleam", 548). -spec build_rects_spaced( direction(), rect(), list(integer()), integer(), integer(), list(rect()) ) -> list(rect()). build_rects_spaced(Direction, Area, Sizes, Spacing, Cursor, Acc) -> Limit = case Direction of vertical -> erlang:element(3, erlang:element(3, Area)); horizontal -> erlang:element(2, erlang:element(3, Area)) end, case Sizes of [] -> lists:reverse(Acc); [Size | Rest] -> Start = gleam@int:min(Cursor, Limit), Clamped = gleam@int:min(Size, gleam@int:max(0, Limit - Start)), Rect = case Direction of vertical -> {rect, {position, erlang:element(2, erlang:element(2, Area)), erlang:element(3, erlang:element(2, Area)) + Start}, {size, erlang:element(2, erlang:element(3, Area)), Clamped}}; horizontal -> {rect, {position, erlang:element(2, erlang:element(2, Area)) + Start, erlang:element(3, erlang:element(2, Area))}, {size, Clamped, erlang:element(3, erlang:element(3, Area))}} end, Next_cursor = case Rest of [] -> Start + Clamped; _ -> (Start + Clamped) + Spacing end, build_rects_spaced( Direction, Area, Rest, Spacing, Next_cursor, [Rect | Acc] ) end. -file("src/etui/geometry.gleam", 526). ?DOC( " Split a rect with `spacing` cells of gap between each child.\n" " Gap cells are taken from the total before distributing to constraints.\n" "\n" " ```gleam\n" " // Two columns with a 1-cell gap\n" " split_with_spacing(Horizontal, area, [Fill, Fill], 1)\n" " ```\n" ). -spec split_with_spacing(direction(), rect(), list(constraint()), integer()) -> list(rect()). split_with_spacing(Direction, Area, Constraints, Spacing) -> N = erlang:length(Constraints), case N =< 1 of true -> split(Direction, Area, Constraints); false -> Gap_total = gleam@int:max(0, Spacing) * (N - 1), Total = case Direction of vertical -> erlang:element(3, erlang:element(3, Area)); horizontal -> erlang:element(2, erlang:element(3, Area)) end, Available = gleam@int:max(0, Total - Gap_total), Sizes = resolve_sizes(Available, Constraints), build_rects_spaced( Direction, Area, Sizes, gleam@int:max(0, Spacing), 0, [] ) end. -file("src/etui/geometry.gleam", 728). -spec build_flex_rects( direction(), rect(), list(integer()), list(integer()), list(rect()) ) -> list(rect()). build_flex_rects(Direction, Area, Sizes, Offsets, Acc) -> case {Sizes, Offsets} of {[], _} -> lists:reverse(Acc); {_, []} -> lists:reverse(Acc); {[Size | Rest_s], [Offset | Rest_o]} -> Rect = case Direction of vertical -> {rect, {position, erlang:element(2, erlang:element(2, Area)), erlang:element(3, erlang:element(2, Area)) + Offset}, {size, erlang:element(2, erlang:element(3, Area)), Size}}; horizontal -> {rect, {position, erlang:element(2, erlang:element(2, Area)) + Offset, erlang:element(3, erlang:element(2, Area))}, {size, Size, erlang:element(3, erlang:element(3, Area))}} end, build_flex_rects(Direction, Area, Rest_s, Rest_o, [Rect | Acc]) end. -file("src/etui/geometry.gleam", 706). -spec around_offsets( list(integer()), integer(), integer(), integer(), list(integer()) ) -> list(integer()). around_offsets(Sizes, Leftover, N, Cursor, Acc) -> Slot = case N of 0 -> 0; _ -> case N of 0 -> 0; Gleam@denominator -> Leftover div Gleam@denominator end end, Half = Slot div 2, case Sizes of [] -> lists:reverse(Acc); [S | Rest] -> Pos = Cursor + Half, Next = ((Pos + S) + Half) + (Slot rem 2), around_offsets(Rest, Leftover, N, Next, [Pos | Acc]) end. -file("src/etui/geometry.gleam", 685). -spec between_offsets( list(integer()), integer(), integer(), integer(), list(integer()) ) -> list(integer()). between_offsets(Sizes, Leftover, N, Cursor, Acc) -> Gaps = gleam@int:max(1, N - 1), Gap_size = case Gaps of 0 -> 0; _ -> case Gaps of 0 -> 0; Gleam@denominator -> Leftover div Gleam@denominator end end, case Sizes of [] -> lists:reverse(Acc); [S | Rest] -> Next = (Cursor + S) + Gap_size, between_offsets(Rest, Leftover, N, Next, [Cursor | Acc]) end. -file("src/etui/geometry.gleam", 670). -spec start_offsets(list(integer()), integer(), integer(), list(integer())) -> list(integer()). start_offsets(Sizes, Gap, Start, Acc) -> case Sizes of [] -> lists:reverse(Acc); [S | Rest] -> Next = (Start + S) + Gap, start_offsets(Rest, Gap, Next, [Start | Acc]) end. -file("src/etui/geometry.gleam", 654). -spec flex_offsets( list(integer()), flex_justify(), integer(), integer(), integer() ) -> list(integer()). flex_offsets(Sizes, Justify, Gap, Leftover, N) -> case Justify of flex_start -> start_offsets(Sizes, Gap, 0, []); flex_end -> start_offsets(Sizes, Gap, Leftover, []); flex_center -> start_offsets(Sizes, Gap, Leftover div 2, []); flex_between -> between_offsets(Sizes, Leftover, N, 0, []); flex_around -> around_offsets(Sizes, Leftover, N, 0, []) end. -file("src/etui/geometry.gleam", 627). ?DOC( " Flex layout: children have fixed sizes (from constraints), leftover space\n" " distributed according to `justify`. Use for toolbars, status bars, centering\n" " a widget in a larger area, or equal-gap grids.\n" "\n" " `gap` is the minimum gap between children (cells). Ignored when `justify`\n" " provides its own spacing (Between/Around). With `FlexStart`/`End`/`Center`,\n" " `gap` acts like `split_with_spacing`'s spacing parameter.\n" "\n" " ```gleam\n" " // Center a 20-wide widget in a 80-wide area:\n" " split_flex(Horizontal, area, [Length(20)], FlexCenter, 0)\n" "\n" " // Three buttons with 2-cell gap between:\n" " split_flex(Horizontal, area, [Length(10), Length(10), Length(10)], FlexStart, 2)\n" "\n" " // Toolbar: left item + right item, space between:\n" " split_flex(Horizontal, area, [Length(10), Length(10)], FlexBetween, 0)\n" " ```\n" ). -spec split_flex( direction(), rect(), list(constraint()), flex_justify(), integer() ) -> list(rect()). split_flex(Direction, Area, Constraints, Justify, Gap) -> N = erlang:length(Constraints), case N =:= 0 of true -> []; false -> Total = case Direction of vertical -> erlang:element(3, erlang:element(3, Area)); horizontal -> erlang:element(2, erlang:element(3, Area)) end, Gap_cells = gleam@int:max(0, Gap) * gleam@int:max(0, N - 1), Available = gleam@int:max(0, Total - Gap_cells), Sizes = resolve_sizes(Available, Constraints), Content_width = gleam@list:fold( Sizes, 0, fun(Acc, S) -> Acc + S end ) + Gap_cells, Leftover = gleam@int:max(0, Total - Content_width), Offsets = flex_offsets(Sizes, Justify, Gap, Leftover, N), build_flex_rects(Direction, Area, Sizes, Offsets, []) end. -file("src/etui/geometry.gleam", 791). -spec pick_breakpoint(list(breakpoint()), integer()) -> list(constraint()). pick_breakpoint(Sorted_desc, Width) -> case Sorted_desc of [] -> []; [Bp] -> erlang:element(3, Bp); [Bp@1 | Rest] -> case Width >= erlang:element(2, Bp@1) of true -> erlang:element(3, Bp@1); false -> pick_breakpoint(Rest, Width) end end. -file("src/etui/geometry.gleam", 774). ?DOC( " Split `area` horizontally using the first breakpoint whose `min_width` <=\n" " `area.size.width`, evaluated in descending order. Falls back to the last\n" " breakpoint (assumed smallest). Returns `[area]` if `breakpoints` is empty.\n" "\n" " Example, two columns on wide screens, stacked on narrow:\n" " ```gleam\n" " geometry.split_responsive(area, [\n" " geometry.Breakpoint(80, [Percentage(50), Percentage(50)]),\n" " geometry.Breakpoint(0, [Percentage(100)]),\n" " ])\n" " ```\n" ). -spec split_responsive(rect(), list(breakpoint())) -> list(rect()). split_responsive(Area, Breakpoints) -> case Breakpoints of [] -> [Area]; _ -> Sorted = gleam@list:sort( Breakpoints, fun(A, B) -> gleam@int:compare( erlang:element(2, B), erlang:element(2, A) ) end ), Chosen = pick_breakpoint( Sorted, erlang:element(2, erlang:element(3, Area)) ), split_h(Area, Chosen) end.