-module(etui@widgets@notification).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/etui/widgets/notification.gleam").
-export([queue_new/1, with_corner/2, info/2, success/2, warning/2, error/2, persistent/2, push/2, tick/1, dismiss_level/2, dismiss_all/1, dismiss_first/1, has_notifications/1, count/1, render/3]).
-export_type([level/0, notification/0, notification_queue/0, corner/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 level() :: info | success | warning | error.
-type notification() :: {notification, binary(), level(), integer()}.
-type notification_queue() :: {notification_queue,
list(notification()),
integer(),
corner()}.
-type corner() :: top_right | top_left | bottom_right | bottom_left.
-file("src/etui/widgets/notification.gleam", 72).
?DOC(" New empty queue. Max = 5, corner = BottomRight.\n").
-spec queue_new(integer()) -> notification_queue().
queue_new(Max) ->
{notification_queue, [], Max, bottom_right}.
-file("src/etui/widgets/notification.gleam", 77).
?DOC(" Set which corner to stack notifications.\n").
-spec with_corner(notification_queue(), corner()) -> notification_queue().
with_corner(Q, Corner) ->
{notification_queue, erlang:element(2, Q), erlang:element(3, Q), Corner}.
-file("src/etui/widgets/notification.gleam", 82).
?DOC(" Build an Info notification.\n").
-spec info(binary(), integer()) -> notification().
info(Message, Ttl) ->
{notification, Message, info, Ttl}.
-file("src/etui/widgets/notification.gleam", 87).
?DOC(" Build a Success notification.\n").
-spec success(binary(), integer()) -> notification().
success(Message, Ttl) ->
{notification, Message, success, Ttl}.
-file("src/etui/widgets/notification.gleam", 92).
?DOC(" Build a Warning notification.\n").
-spec warning(binary(), integer()) -> notification().
warning(Message, Ttl) ->
{notification, Message, warning, Ttl}.
-file("src/etui/widgets/notification.gleam", 97).
?DOC(" Build an Error notification (persistent by default: ttl = -1).\n").
-spec error(binary(), integer()) -> notification().
error(Message, Ttl) ->
{notification, Message, error, Ttl}.
-file("src/etui/widgets/notification.gleam", 102).
?DOC(" Persistent notification (never auto-expires; dismiss manually).\n").
-spec persistent(binary(), level()) -> notification().
persistent(Message, Level) ->
{notification, Message, Level, -1}.
-file("src/etui/widgets/notification.gleam", 110).
?DOC(" Add a notification. If queue is full, the oldest is dropped.\n").
-spec push(notification_queue(), notification()) -> notification_queue().
push(Q, N) ->
Items = lists:append(erlang:element(2, Q), [N]),
Trimmed = case erlang:length(Items) > erlang:element(3, Q) of
true ->
gleam@list:drop(Items, erlang:length(Items) - erlang:element(3, Q));
false ->
Items
end,
{notification_queue, Trimmed, erlang:element(3, Q), erlang:element(4, Q)}.
-file("src/etui/widgets/notification.gleam", 121).
?DOC(
" Advance time by one tick. Decrements TTL on all non-persistent\n"
" notifications and removes expired ones (ttl == 0).\n"
).
-spec tick(notification_queue()) -> notification_queue().
tick(Q) ->
Items = begin
_pipe = erlang:element(2, Q),
_pipe@1 = gleam@list:map(_pipe, fun(N) -> case erlang:element(4, N) of
-1 ->
N;
T ->
{notification,
erlang:element(2, N),
erlang:element(3, N),
T - 1}
end end),
gleam@list:filter(_pipe@1, fun(N@1) -> erlang:element(4, N@1) /= 0 end)
end,
{notification_queue, Items, erlang:element(3, Q), erlang:element(4, Q)}.
-file("src/etui/widgets/notification.gleam", 135).
?DOC(" Dismiss all notifications matching `level`.\n").
-spec dismiss_level(notification_queue(), level()) -> notification_queue().
dismiss_level(Q, Level) ->
{notification_queue,
gleam@list:filter(
erlang:element(2, Q),
fun(N) -> erlang:element(3, N) /= Level end
),
erlang:element(3, Q),
erlang:element(4, Q)}.
-file("src/etui/widgets/notification.gleam", 143).
?DOC(" Dismiss all notifications.\n").
-spec dismiss_all(notification_queue()) -> notification_queue().
dismiss_all(Q) ->
{notification_queue, [], erlang:element(3, Q), erlang:element(4, Q)}.
-file("src/etui/widgets/notification.gleam", 148).
?DOC(" Dismiss the oldest notification.\n").
-spec dismiss_first(notification_queue()) -> notification_queue().
dismiss_first(Q) ->
case erlang:element(2, Q) of
[] ->
Q;
[_ | Rest] ->
{notification_queue,
Rest,
erlang:element(3, Q),
erlang:element(4, Q)}
end.
-file("src/etui/widgets/notification.gleam", 156).
?DOC(" True if there are active notifications.\n").
-spec has_notifications(notification_queue()) -> boolean().
has_notifications(Q) ->
not gleam@list:is_empty(erlang:element(2, Q)).
-file("src/etui/widgets/notification.gleam", 161).
?DOC(" Count of active notifications.\n").
-spec count(notification_queue()) -> integer().
count(Q) ->
erlang:length(erlang:element(2, Q)).
-file("src/etui/widgets/notification.gleam", 264).
-spec level_colors(level()) -> {etui@style:color(), etui@style:color()}.
level_colors(Level) ->
case Level of
info ->
{{indexed, 15}, {indexed, 4}};
success ->
{{indexed, 15}, {indexed, 2}};
warning ->
{{indexed, 0}, {indexed, 3}};
error ->
{{indexed, 15}, {indexed, 1}}
end.
-file("src/etui/widgets/notification.gleam", 181).
-spec render_items(
etui@buffer:buffer(),
etui@geometry:rect(),
list(notification()),
corner(),
integer()
) -> etui@buffer:buffer().
render_items(Buf, Area, Items, Corner, Index) ->
case Items of
[] ->
Buf;
[N | Rest] ->
Box_h = 3,
Msg_w = etui@text:cell_width(erlang:element(2, N)),
Box_w = case (Msg_w + 4) > 30 of
true ->
case (Msg_w + 4) > erlang:element(
2,
erlang:element(3, Area)
) of
true ->
erlang:element(2, erlang:element(3, Area));
false ->
Msg_w + 4
end;
false ->
30
end,
{X, Y} = case Corner of
top_right ->
{(erlang:element(2, erlang:element(2, Area)) + erlang:element(
2,
erlang:element(3, Area)
))
- Box_w,
erlang:element(3, erlang:element(2, Area)) + (Index * Box_h)};
top_left ->
{erlang:element(2, erlang:element(2, Area)),
erlang:element(3, erlang:element(2, Area)) + (Index * Box_h)};
bottom_right ->
{(erlang:element(2, erlang:element(2, Area)) + erlang:element(
2,
erlang:element(3, Area)
))
- Box_w,
((erlang:element(3, erlang:element(2, Area)) + erlang:element(
3,
erlang:element(3, Area)
))
- Box_h)
- (Index * Box_h)};
bottom_left ->
{erlang:element(2, erlang:element(2, Area)),
((erlang:element(3, erlang:element(2, Area)) + erlang:element(
3,
erlang:element(3, Area)
))
- Box_h)
- (Index * Box_h)}
end,
Fits = (((X >= erlang:element(2, erlang:element(2, Area))) andalso (Y
>= erlang:element(3, erlang:element(2, Area))))
andalso ((X + Box_w) =< (erlang:element(2, erlang:element(2, Area))
+ erlang:element(2, erlang:element(3, Area)))))
andalso ((Y + Box_h) =< (erlang:element(3, erlang:element(2, Area))
+ erlang:element(3, erlang:element(3, Area)))),
Buf2 = case Fits of
false ->
Buf;
true ->
Box_area = {rect, {position, X, Y}, {size, Box_w, Box_h}},
{Fg, Bg} = level_colors(erlang:element(3, N)),
Blk = begin
_pipe = etui@widgets@block:block_new(),
_pipe@1 = etui@widgets@block:with_border(_pipe, rounded),
_pipe@2 = etui@widgets@block:with_style(_pipe@1, Fg, Bg),
etui@widgets@block:with_bg_fill(_pipe@2)
end,
Buf_b = etui@widgets@block:render(Buf, Box_area, Blk),
Inner = etui@widgets@block:inner(Box_area, Blk),
Msg_x = erlang:element(2, erlang:element(2, Inner)) + ((erlang:element(
2,
erlang:element(3, Inner)
)
- etui@text:cell_width(erlang:element(2, N)))
div 2),
case (erlang:element(2, erlang:element(3, Inner)) > 0)
andalso (erlang:element(3, erlang:element(3, Inner)) > 0) of
false ->
Buf_b;
true ->
etui@buffer:set_string(
Buf_b,
{position,
Msg_x,
erlang:element(3, erlang:element(2, Inner))},
etui@text:truncate(
erlang:element(2, N),
erlang:element(2, erlang:element(3, Inner)),
<<"…"/utf8>>
),
Fg,
Bg,
etui@style:none()
)
end
end,
render_items(Buf2, Area, Rest, Corner, Index + 1)
end.
-file("src/etui/widgets/notification.gleam", 170).
?DOC(
" Render all active notifications stacked in the configured corner.\n"
" Each notification is a single-row bordered box; they stack inward.\n"
).
-spec render(etui@buffer:buffer(), etui@geometry:rect(), notification_queue()) -> etui@buffer:buffer().
render(Buf, Area, Q) ->
case (gleam@list:is_empty(erlang:element(2, Q)) orelse (erlang:element(
2,
erlang:element(3, Area)
)
=< 0))
orelse (erlang:element(3, erlang:element(3, Area)) =< 0) of
true ->
Buf;
false ->
render_items(
Buf,
Area,
erlang:element(2, Q),
erlang:element(4, Q),
0
)
end.