-module(etui@widgets@list).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/etui/widgets/list.gleam").
-export([list_new/1, list_new_styled/1, with_colors/3, with_highlight_style/2, with_style/2, with_blink/2, state_new/0, select/2, select_next/2, select_prev/1, clamp_state/2, render/3, render_stateful/4, render_animated/5, effective_offset/2]).
-export_type([list_widget/0, list_state/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 list_widget() :: {list_widget,
list(etui@span:line()),
etui@style:color(),
etui@style:color(),
etui@style:style(),
integer()}.
-type list_state() :: {list_state, integer(), integer()}.
-file("src/etui/widgets/list.gleam", 34).
?DOC(" New list from plain strings. Default colors, reverse-video selection.\n").
-spec list_new(list(binary())) -> list_widget().
list_new(Items) ->
{list_widget,
gleam@list:map(Items, fun etui@span:line_plain/1),
default,
default,
{style, default, default, etui@style:reverse()},
0}.
-file("src/etui/widgets/list.gleam", 56).
?DOC(
" New list from styled `span.Line` items.\n"
"\n"
" ```gleam\n"
" list.list_new_styled([\n"
" span.line_new([span.span_styled(\"ERROR\", style.bold_style()), span.span_plain(\" file\")]),\n"
" span.line_plain(\"normal item\"),\n"
" ])\n"
" ```\n"
).
-spec list_new_styled(list(etui@span:line())) -> list_widget().
list_new_styled(Items) ->
{list_widget,
Items,
default,
default,
{style, default, default, etui@style:reverse()},
0}.
-file("src/etui/widgets/list.gleam", 70).
-spec with_colors(list_widget(), etui@style:color(), etui@style:color()) -> list_widget().
with_colors(L, Fg, Bg) ->
{list_widget,
erlang:element(2, L),
Fg,
Bg,
erlang:element(5, L),
erlang:element(6, L)}.
-file("src/etui/widgets/list.gleam", 78).
-spec with_highlight_style(list_widget(), etui@style:style()) -> list_widget().
with_highlight_style(L, S) ->
{list_widget,
erlang:element(2, L),
erlang:element(3, L),
erlang:element(4, L),
S,
erlang:element(6, L)}.
-file("src/etui/widgets/list.gleam", 82).
-spec with_style(list_widget(), etui@style:style()) -> list_widget().
with_style(L, S) ->
{list_widget,
erlang:element(2, L),
erlang:element(2, S),
erlang:element(3, S),
erlang:element(5, L),
erlang:element(6, L)}.
-file("src/etui/widgets/list.gleam", 87).
?DOC(" Blink period in frames. 0 = steady (no blink). Use with `render_animated`.\n").
-spec with_blink(list_widget(), integer()) -> list_widget().
with_blink(L, Period) ->
{list_widget,
erlang:element(2, L),
erlang:element(3, L),
erlang:element(4, L),
erlang:element(5, L),
Period}.
-file("src/etui/widgets/list.gleam", 94).
-spec state_new() -> list_state().
state_new() ->
{list_state, 0, 0}.
-file("src/etui/widgets/list.gleam", 98).
-spec select(list_state(), integer()) -> list_state().
select(State, Idx) ->
{list_state, gleam@int:max(0, Idx), erlang:element(3, State)}.
-file("src/etui/widgets/list.gleam", 102).
-spec select_next(list_state(), integer()) -> list_state().
select_next(State, Item_count) ->
Max_idx = gleam@int:max(0, Item_count - 1),
{list_state,
gleam@int:min(Max_idx, erlang:element(2, State) + 1),
erlang:element(3, State)}.
-file("src/etui/widgets/list.gleam", 107).
-spec select_prev(list_state()) -> list_state().
select_prev(State) ->
{list_state,
gleam@int:max(0, erlang:element(2, State) - 1),
erlang:element(3, State)}.
-file("src/etui/widgets/list.gleam", 113).
?DOC(
" Clamp `selected` to `[0, item_count - 1]`.\n"
" Call after replacing the item list to avoid a stale selection index.\n"
).
-spec clamp_state(list_state(), integer()) -> list_state().
clamp_state(State, Item_count) ->
Max = gleam@int:max(0, Item_count - 1),
{list_state,
gleam@int:min(erlang:element(2, State), Max),
erlang:element(3, State)}.
-file("src/etui/widgets/list.gleam", 239).
-spec apply_highlight_to_line(etui@span:line(), etui@style:style()) -> etui@span:line().
apply_highlight_to_line(Line, Hl) ->
etui@span:line_new(
gleam@list:map(
erlang:element(2, Line),
fun(Sp) ->
New_fg = case erlang:element(3, Sp) of
default ->
erlang:element(2, Hl);
_ ->
erlang:element(3, Sp)
end,
New_bg = case erlang:element(4, Sp) of
default ->
erlang:element(3, Hl);
_ ->
erlang:element(4, Sp)
end,
New_mod = case etui@style:modifier_equal(
erlang:element(5, Sp),
etui@style:none()
) of
true ->
erlang:element(4, Hl);
false ->
erlang:element(5, Sp)
end,
{span,
erlang:element(2, Sp),
New_fg,
New_bg,
New_mod,
erlang:element(6, Sp)}
end
)
).
-file("src/etui/widgets/list.gleam", 284).
-spec get_item_at(list(etui@span:line()), integer()) -> {ok, etui@span:line()} |
{error, nil}.
get_item_at(Items, Idx) ->
case Idx of
I when I < 0 ->
{error, nil};
0 ->
case Items of
[H | _] ->
{ok, H};
[] ->
{error, nil}
end;
_ ->
get_item_at(gleam@list:drop(Items, 1), Idx - 1)
end.
-file("src/etui/widgets/list.gleam", 168).
-spec do_render(
etui@buffer:buffer(),
etui@geometry:rect(),
list_widget(),
integer(),
integer(),
integer()
) -> etui@buffer:buffer().
do_render(Buf, Area, L, Selected, Offset, Y_offset) ->
case Y_offset >= erlang:element(3, erlang:element(3, Area)) of
true ->
Buf;
false ->
Item_idx = Offset + Y_offset,
Y = erlang:element(3, erlang:element(2, Area)) + Y_offset,
Is_selected = Item_idx =:= Selected,
{Row_fg, Row_bg, Row_mod} = case Is_selected of
true ->
{erlang:element(2, erlang:element(5, L)),
erlang:element(3, erlang:element(5, L)),
erlang:element(4, erlang:element(5, L))};
false ->
{erlang:element(3, L),
erlang:element(4, L),
etui@style:none()}
end,
Bg_row = etui@text:pad_right(
<<""/utf8>>,
erlang:element(2, erlang:element(3, Area))
),
Buf1 = etui@buffer:set_string(
Buf,
{position, erlang:element(2, erlang:element(2, Area)), Y},
Bg_row,
Row_fg,
Row_bg,
Row_mod
),
Buf2 = case get_item_at(erlang:element(2, L), Item_idx) of
{error, _} ->
Buf1;
{ok, Line} ->
Prefix = case Is_selected of
true ->
<<"▶ "/utf8>>;
false ->
<<" "/utf8>>
end,
Prefix_w = etui@text:cell_width(Prefix),
Buf3 = etui@buffer:set_string(
Buf1,
{position,
erlang:element(2, erlang:element(2, Area)),
Y},
Prefix,
Row_fg,
Row_bg,
Row_mod
),
Effective_line = case Is_selected of
false ->
Line;
true ->
apply_highlight_to_line(Line, erlang:element(5, L))
end,
etui@span:render_line(
Buf3,
{position,
erlang:element(2, erlang:element(2, Area)) + Prefix_w,
Y},
Effective_line,
erlang:element(2, erlang:element(3, Area)) - Prefix_w
)
end,
do_render(Buf2, Area, L, Selected, Offset, Y_offset + 1)
end.
-file("src/etui/widgets/list.gleam", 121).
-spec render(etui@buffer:buffer(), etui@geometry:rect(), list_widget()) -> etui@buffer:buffer().
render(Buf, Area, L) ->
case erlang:element(3, erlang:element(3, Area)) =< 0 of
true ->
Buf;
false ->
do_render(Buf, Area, L, -1, 0, 0)
end.
-file("src/etui/widgets/list.gleam", 266).
-spec scroll_offset(integer(), integer(), integer()) -> integer().
scroll_offset(Selected, Offset, Height) ->
case Selected < Offset of
true ->
Selected;
false ->
case Height =< 0 of
true ->
Offset;
false ->
case Selected >= (Offset + Height) of
true ->
(Selected - Height) + 1;
false ->
Offset
end
end
end.
-file("src/etui/widgets/list.gleam", 132).
-spec render_stateful(
etui@buffer:buffer(),
etui@geometry:rect(),
list_widget(),
list_state()
) -> etui@buffer:buffer().
render_stateful(Buf, Area, L, State) ->
case erlang:element(3, erlang:element(3, Area)) =< 0 of
true ->
Buf;
false ->
Offset = scroll_offset(
erlang:element(2, State),
erlang:element(3, State),
erlang:element(3, erlang:element(3, Area))
),
do_render(Buf, Area, L, erlang:element(2, State), Offset, 0)
end.
-file("src/etui/widgets/list.gleam", 147).
-spec render_animated(
etui@buffer:buffer(),
etui@geometry:rect(),
list_widget(),
list_state(),
integer()
) -> etui@buffer:buffer().
render_animated(Buf, Area, L, State, Frame) ->
case erlang:element(3, erlang:element(3, Area)) =< 0 of
true ->
Buf;
false ->
Offset = scroll_offset(
erlang:element(2, State),
erlang:element(3, State),
erlang:element(3, erlang:element(3, Area))
),
Show = etui@anim:blink(Frame, erlang:element(6, L)),
Sel = case Show of
true ->
erlang:element(2, State);
false ->
-1
end,
do_render(Buf, Area, L, Sel, Offset, 0)
end.
-file("src/etui/widgets/list.gleam", 262).
-spec effective_offset(list_state(), integer()) -> integer().
effective_offset(State, Height) ->
scroll_offset(erlang:element(2, State), erlang:element(3, State), Height).