Skip to main content

src/etui@widgets@paragraph.erl

-module(etui@widgets@paragraph).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/etui/widgets/paragraph.gleam").
-export([paragraph_new/1, with_alignment/2, with_style/2, paragraph_new_lines/1, render_styled/3, render_lines_styled/3, render/3]).
-export_type([paragraph/0, span_paragraph/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 paragraph() :: {paragraph,
        binary(),
        etui@text:alignment(),
        etui@style:color(),
        etui@style:color(),
        etui@style:modifier()}.

-type span_paragraph() :: {span_paragraph, list(etui@span:line())}.

-file("src/etui/widgets/paragraph.gleam", 24).
?DOC(" New paragraph with left-aligned text and default colors.\n").
-spec paragraph_new(binary()) -> paragraph().
paragraph_new(Text) ->
    {paragraph, Text, left, default, default, etui@style:none()}.

-file("src/etui/widgets/paragraph.gleam", 35).
?DOC(" Set text alignment (Left, Center, Right).\n").
-spec with_alignment(paragraph(), etui@text:alignment()) -> paragraph().
with_alignment(P, Alignment) ->
    {paragraph,
        erlang:element(2, P),
        Alignment,
        erlang:element(4, P),
        erlang:element(5, P),
        erlang:element(6, P)}.

-file("src/etui/widgets/paragraph.gleam", 40).
?DOC(" Apply a style (colors + modifier) to the paragraph text.\n").
-spec with_style(paragraph(), etui@style:style()) -> paragraph().
with_style(P, S) ->
    {paragraph,
        erlang:element(2, P),
        erlang:element(3, P),
        erlang:element(2, S),
        erlang:element(3, S),
        erlang:element(4, S)}.

-file("src/etui/widgets/paragraph.gleam", 62).
?DOC(
    " Build a `SpanParagraph` from a list of `span.Line` values.\n"
    "\n"
    " ```gleam\n"
    " paragraph.paragraph_new_lines([\n"
    "   span.line_new([span.span_plain(\"normal \"), span.span_styled(\"bold\", style.bold_style())]),\n"
    "   span.line_plain(\"second line\"),\n"
    " ])\n"
    " |> paragraph.render_lines_styled(buf, area, _)\n"
    " ```\n"
).
-spec paragraph_new_lines(list(etui@span:line())) -> span_paragraph().
paragraph_new_lines(Lines) ->
    {span_paragraph, Lines}.

-file("src/etui/widgets/paragraph.gleam", 89).
-spec render_span_rows(
    etui@buffer:buffer(),
    etui@geometry:rect(),
    list(etui@span:line()),
    integer()
) -> etui@buffer:buffer().
render_span_rows(Buf, Area, Lines, Row) ->
    case Lines of
        [] ->
            Buf;

        [Line | Rest] ->
            case Row >= erlang:element(3, erlang:element(3, Area)) of
                true ->
                    Buf;

                false ->
                    Pos = {position,
                        erlang:element(2, erlang:element(2, Area)),
                        erlang:element(3, erlang:element(2, Area)) + Row},
                    Buf2 = etui@span:render_line(
                        Buf,
                        Pos,
                        Line,
                        erlang:element(2, erlang:element(3, Area))
                    ),
                    render_span_rows(Buf2, Area, Rest, Row + 1)
            end
    end.

-file("src/etui/widgets/paragraph.gleam", 78).
?DOC(
    " Render a list of `span.Line` values, one per row, into `area`.\n"
    " Each `Line` is drawn with per-span styles. Lines beyond area height\n"
    " are clipped; the list may be shorter than the area (remaining rows unchanged).\n"
).
-spec render_styled(
    etui@buffer:buffer(),
    etui@geometry:rect(),
    list(etui@span:line())
) -> etui@buffer:buffer().
render_styled(Buf, Area, Lines) ->
    case (erlang:element(2, erlang:element(3, Area)) =< 0) orelse (erlang:element(
        3,
        erlang:element(3, Area)
    )
    =< 0) of
        true ->
            Buf;

        false ->
            render_span_rows(Buf, Area, Lines, 0)
    end.

-file("src/etui/widgets/paragraph.gleam", 67).
?DOC(" Render a `SpanParagraph` into `area`. Lines beyond area height are clipped.\n").
-spec render_lines_styled(
    etui@buffer:buffer(),
    etui@geometry:rect(),
    span_paragraph()
) -> etui@buffer:buffer().
render_lines_styled(Buf, Area, Para) ->
    render_styled(Buf, Area, erlang:element(2, Para)).

-file("src/etui/widgets/paragraph.gleam", 131).
-spec render_lines(
    etui@buffer:buffer(),
    etui@geometry:rect(),
    paragraph(),
    list(binary()),
    integer()
) -> etui@buffer:buffer().
render_lines(Buf, Area, Para, Lines, Line_idx) ->
    case Lines of
        [] ->
            Buf;

        [Line | Rest] ->
            case Line_idx >= erlang:element(3, erlang:element(3, Area)) of
                true ->
                    Buf;

                false ->
                    Y = erlang:element(3, erlang:element(2, Area)) + Line_idx,
                    Aligned_line = etui@text:align(
                        Line,
                        erlang:element(2, erlang:element(3, Area)),
                        erlang:element(3, Para)
                    ),
                    Buf_new = etui@buffer:set_string(
                        Buf,
                        {position,
                            erlang:element(2, erlang:element(2, Area)),
                            Y},
                        Aligned_line,
                        erlang:element(4, Para),
                        erlang:element(5, Para),
                        erlang:element(6, Para)
                    ),
                    render_lines(Buf_new, Area, Para, Rest, Line_idx + 1)
            end
    end.

-file("src/etui/widgets/paragraph.gleam", 115).
?DOC(
    " Render paragraph into buffer at `area`. Word-wraps to area width.\n"
    " Rows beyond area height are clipped. Short lines are padded to area width.\n"
).
-spec render(etui@buffer:buffer(), etui@geometry:rect(), paragraph()) -> etui@buffer:buffer().
render(Buf, Area, Para) ->
    case (erlang:element(2, erlang:element(3, Area)) =< 0) orelse (erlang:element(
        3,
        erlang:element(3, Area)
    )
    =< 0) of
        true ->
            Buf;

        false ->
            Lines = etui@text:wrap(
                erlang:element(2, Para),
                erlang:element(2, erlang:element(3, Area))
            ),
            render_lines(Buf, Area, Para, Lines, 0)
    end.