%% vim: ts=4 sw=4 et
-module(boxer).
-export([
print/1,
print/2,
wrap/1,
wrap/2,
add_line_def/2,
get_line_def/1,
get_line_char/2
]).
-type line_def_name() :: term().
-type char_key() :: horiz | vert | top_left | top_right | bottom_left | bottom_right | integer().
print(Msg) ->
io:format(wrap(Msg)).
print(Msg, LineDefName) ->
io:format(wrap(Msg, LineDefName)).
wrap(Msg) ->
DefaultLineDef = application:get_env(boxer, default_line_def, default),
wrap(Msg, DefaultLineDef).
wrap(Msg0, LineDefName) ->
Msg = unicode:characters_to_list(Msg0),
[H,V,UL,UR,BR,BL] = get_line_def_chars(LineDefName),
HPadding = get_h_padding(LineDefName),
VPadding = get_v_padding(LineDefName),
Pad = lists:duplicate(HPadding, $\s),
VPad = lists:duplicate(VPadding, $\n),
MsgLines = string:split(lists:flatten([VPad, Msg, VPad]), "\n", all),
LongestLine = lists:max([length(X) || X <- MsgLines]) ,
TotalWidth = LongestLine + (HPadding * 2),
MsgLines2 = [pad_end(string:chomp(X), LongestLine) || X <- MsgLines],
lists:flatten([
make_row(TotalWidth, UL, H, UR),"\n",
[add_verts([Pad, Line, Pad], V) ++ "\n" || Line <- MsgLines2],
make_row(TotalWidth, BL, H, BR),"\n"
]).
get_line_defs() ->
case persistent_term:get(boxer_line_defs, undefined) of
undefined ->
LineDefs = application:get_env(boxer, line_defs, []),
persistent_term:put(boxer_line_defs, LineDefs),
LineDefs;
LineDefs ->
LineDefs
end.
put_line_defs(LineDefs) ->
persistent_term:put(boxer_line_defs, LineDefs).
add_line_def(LineDefName, LookupStr) ->
LineDefs = get_line_defs(),
LineDefs2 = lists:keydelete(LineDefName, 1, LineDefs),
LineDefs3 = [{LineDefName, LookupStr} | LineDefs2],
put_line_defs(LineDefs3).
get_line_def(LineDefName) ->
LineDefs = get_line_defs(),
case proplists:get_value(LineDefName, LineDefs, undefined) of
undefined -> default_chars(LineDefName);
X -> X
end.
get_line_def_chars(LineDefName) ->
Def = get_line_def(LineDefName),
maps:get(chars, Def, default_chars(default)).
-spec get_line_char(LineDefName :: line_def_name(), Key :: char_key()) -> char().
get_line_char(LineDefName, Key) ->
Pos = keymap(Key),
Chars = get_line_def_chars(LineDefName),
lists:nth(Pos, Chars).
get_h_padding(LineDefName) ->
Def = get_line_def(LineDefName),
maps:get(h_padding, Def, 0).
get_v_padding(LineDefName) ->
Def = get_line_def(LineDefName),
maps:get(v_padding, Def, 0).
keymap(horiz) -> 1;
keymap(vert) -> 2;
keymap(top_left) -> 3;
keymap(top_right) -> 4;
keymap(bottom_left) -> 5;
keymap(bottom_right) -> 6;
keymap(X) when is_integer(X) -> X.
pad_end(Line, ToLength) when length(Line) >= ToLength ->
Line;
pad_end(Line, ToLength) ->
ToAdd = ToLength - length(Line),
Padding = lists:duplicate(ToAdd, $\s),
Line ++ Padding.
%% The total length of this row will be LineWidth+2
make_row(LineWidth, Left, Horiz, Right) ->
lists:flatten([Left, lists:duplicate(LineWidth, Horiz), Right]).
add_verts(Line, Vert) ->
lists:flatten([Vert, Line, Vert]).
% chars are in arranged:
% 1 first: straight lines (horiz, then vert), then
% 2) Corners in CSS corner order: upper-left, upper-right, bottom-right, bottom-left.
default_chars(double) ->
#{h_padding=>0, chars=>"═║╔╗╝╚"};
default_chars(single) ->
#{h_padding=>0, chars=>"─│┌┐┘└"};
default_chars(hash) ->
#{v_padding=>1, h_padding=>2, chars=>"######"};
default_chars(_) ->
default_chars(single).
%replace_tabs_with_spaces(Bin) when is_binary(Bin) ->
% replace_tabs_with_spaces(unicode:characters_to_list(Bin));
%replace_tabs_with_spaces([$\t|T]) ->
% " " ++ replace_tabs_with_spaces(T);
%replace_tabs_with_spaces([H|T]) ->
% [H|replace_tabs_with_spaces(T)];
%replace_tabs_with_spaces([]) ->
% [].