-module(etui@widgets@scroll_view).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/etui/widgets/scroll_view.gleam").
-export([scroll_view_new/2, sv_state_new/0, scroll_to/3, scroll_down/2, scroll_up/2, scroll_right/2, scroll_left/2, clamp/4, render/5, scroll_pct_y/3, scroll_pct_x/3]).
-export_type([scroll_view/0, scroll_view_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 scroll_view() :: {scroll_view, integer(), integer()}.
-type scroll_view_state() :: {scroll_view_state, integer(), integer()}.
-file("src/etui/widgets/scroll_view.gleam", 39).
-spec scroll_view_new(integer(), integer()) -> scroll_view().
scroll_view_new(Virtual_width, Virtual_height) ->
{scroll_view,
gleam@int:max(1, Virtual_width),
gleam@int:max(1, Virtual_height)}.
-file("src/etui/widgets/scroll_view.gleam", 46).
-spec sv_state_new() -> scroll_view_state().
sv_state_new() ->
{scroll_view_state, 0, 0}.
-file("src/etui/widgets/scroll_view.gleam", 50).
-spec scroll_to(scroll_view_state(), integer(), integer()) -> scroll_view_state().
scroll_to(_, X, Y) ->
{scroll_view_state, gleam@int:max(0, X), gleam@int:max(0, Y)}.
-file("src/etui/widgets/scroll_view.gleam", 54).
-spec scroll_down(scroll_view_state(), integer()) -> scroll_view_state().
scroll_down(State, Lines) ->
{scroll_view_state,
erlang:element(2, State),
erlang:element(3, State) + gleam@int:max(0, Lines)}.
-file("src/etui/widgets/scroll_view.gleam", 58).
-spec scroll_up(scroll_view_state(), integer()) -> scroll_view_state().
scroll_up(State, Lines) ->
{scroll_view_state,
erlang:element(2, State),
gleam@int:max(0, erlang:element(3, State) - Lines)}.
-file("src/etui/widgets/scroll_view.gleam", 62).
-spec scroll_right(scroll_view_state(), integer()) -> scroll_view_state().
scroll_right(State, Cols) ->
{scroll_view_state,
erlang:element(2, State) + gleam@int:max(0, Cols),
erlang:element(3, State)}.
-file("src/etui/widgets/scroll_view.gleam", 66).
-spec scroll_left(scroll_view_state(), integer()) -> scroll_view_state().
scroll_left(State, Cols) ->
{scroll_view_state,
gleam@int:max(0, erlang:element(2, State) - Cols),
erlang:element(3, State)}.
-file("src/etui/widgets/scroll_view.gleam", 71).
?DOC(" Clamp scroll offsets so the viewport never goes past the virtual canvas.\n").
-spec clamp(scroll_view_state(), scroll_view(), integer(), integer()) -> scroll_view_state().
clamp(State, Sv, Visible_w, Visible_h) ->
Max_x = gleam@int:max(0, erlang:element(2, Sv) - Visible_w),
Max_y = gleam@int:max(0, erlang:element(3, Sv) - Visible_h),
{scroll_view_state,
gleam@int:min(erlang:element(2, State), Max_x),
gleam@int:min(erlang:element(3, State), Max_y)}.
-file("src/etui/widgets/scroll_view.gleam", 146).
-spec blit_row(
etui@buffer:buffer(),
etui@buffer:buffer(),
etui@geometry:rect(),
integer(),
integer(),
integer(),
integer(),
integer()
) -> etui@buffer:buffer().
blit_row(Buf, Virtual_buf, Area, Ox, Oy, Vis_w, Row, Col) ->
case Col >= Vis_w of
true ->
Buf;
false ->
Src_pos = {position, Ox + Col, Oy + Row},
Dst_pos = {position,
erlang:element(2, erlang:element(2, Area)) + Col,
erlang:element(3, erlang:element(2, Area)) + Row},
Cell = etui@buffer:get_cell(Virtual_buf, Src_pos),
blit_row(
etui@buffer:set_cell(Buf, Dst_pos, Cell),
Virtual_buf,
Area,
Ox,
Oy,
Vis_w,
Row,
Col + 1
)
end.
-file("src/etui/widgets/scroll_view.gleam", 120).
-spec blit(
etui@buffer:buffer(),
etui@buffer:buffer(),
etui@geometry:rect(),
integer(),
integer(),
integer(),
integer(),
integer()
) -> etui@buffer:buffer().
blit(Buf, Virtual_buf, Area, Ox, Oy, Vis_w, Vis_h, Row) ->
case Row >= Vis_h of
true ->
Buf;
false ->
blit(
blit_row(Buf, Virtual_buf, Area, Ox, Oy, Vis_w, Row, 0),
Virtual_buf,
Area,
Ox,
Oy,
Vis_w,
Vis_h,
Row + 1
)
end.
-file("src/etui/widgets/scroll_view.gleam", 93).
?DOC(
" Render the scroll view.\n"
"\n"
" `render_inner` is called with a virtual buffer sized to\n"
" `(sv.virtual_width × sv.virtual_height)`. The visible window at\n"
" `(state.scroll_x, state.scroll_y)` is then blitted into `buf` at `area`.\n"
).
-spec render(
etui@buffer:buffer(),
etui@geometry:rect(),
scroll_view(),
scroll_view_state(),
fun((etui@buffer:buffer(), etui@geometry:rect()) -> etui@buffer:buffer())
) -> etui@buffer:buffer().
render(Buf, Area, Sv, State, Render_inner) ->
case (erlang:element(2, erlang:element(3, Area)) =< 0) orelse (erlang:element(
3,
erlang:element(3, Area)
)
=< 0) of
true ->
Buf;
false ->
Virtual_area = etui@geometry:rect_new(
0,
0,
erlang:element(2, Sv),
erlang:element(3, Sv)
),
Virtual_buf = begin
_pipe = etui@buffer:buffer_new(Virtual_area),
Render_inner(_pipe, Virtual_area)
end,
Vis_w = erlang:element(2, erlang:element(3, Area)),
Vis_h = erlang:element(3, erlang:element(3, Area)),
Ox = gleam@int:max(0, erlang:element(2, State)),
Oy = gleam@int:max(0, erlang:element(3, State)),
blit(Buf, Virtual_buf, Area, Ox, Oy, Vis_w, Vis_h, 0)
end.
-file("src/etui/widgets/scroll_view.gleam", 182).
?DOC(
" How far into the virtual canvas is the viewport (0.0–1.0 × 100).\n"
" Returns an integer percentage (0–100). Useful for driving scrollbar widgets.\n"
).
-spec scroll_pct_y(scroll_view_state(), scroll_view(), integer()) -> integer().
scroll_pct_y(State, Sv, Visible_h) ->
Max_scroll = gleam@int:max(1, erlang:element(3, Sv) - Visible_h),
gleam@int:min(100, case Max_scroll of
0 -> 0;
Gleam@denominator -> erlang:element(3, State) * 100 div Gleam@denominator
end).
-file("src/etui/widgets/scroll_view.gleam", 191).
-spec scroll_pct_x(scroll_view_state(), scroll_view(), integer()) -> integer().
scroll_pct_x(State, Sv, Visible_w) ->
Max_scroll = gleam@int:max(1, erlang:element(2, Sv) - Visible_w),
gleam@int:min(100, case Max_scroll of
0 -> 0;
Gleam@denominator -> erlang:element(2, State) * 100 div Gleam@denominator
end).