-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).