5 Commits

Author SHA1 Message Date
zxq9 355f50c2be WIP: Monkeypatch to make simple args callable for now 2025-03-11 23:00:06 +09:00
zxq9 dc4b05c938 WIP
- Layout works, but is goofy (expansion directions are not managed well in nested input fields)
- "Add" button doesn't do anything yet
- Input types need more specific widgets
- No input typechecking is done yet (not with any meaningful output to the user)
2025-03-11 15:20:05 +09:00
zxq9 2f43d50dcb WIP 2025-03-07 07:16:34 +09:00
zxq9 ef4ca0907c Merge branch 'master' into args 2025-03-07 07:09:13 +09:00
zxq9 138fe0697d WIP 2025-03-07 07:08:46 +09:00
+186 -79
View File
@@ -416,12 +416,11 @@ call_params([{L, C} | T], A) ->
clicked4(State, clicked4(State,
#c{id = ConID, build = #{aci := ACI}, funs = {_, Funs}}, #c{id = ConID, build = #{aci := ACI}, funs = {_, Funs}},
{Name, Type}, {Name, Type},
{PK, Nonce, TTL, GasP, Gas, Amount}) -> {PK, Nonce, TTL, GasP, Gas, Amt}) ->
AACI = hz:prepare_aaci(ACI), AACI = hz:prepare_aaci(ACI),
#f{args = ArgFields} = lists:keyfind(Name, #f.name, Funs), #f{args = ArgFields} = maps:get(Name, Funs),
Args = lists:map(fun get_arg/1, ArgFields), Args = lists:map(fun get_arg/1, ArgFields),
FunName = binary_to_list(Name), case hz:contract_call(PK, Nonce, Gas, GasP, Amt, TTL, AACI, ConID, Name, Args) of
case hz:contract_call(PK, Nonce, Gas, GasP, Amount, TTL, AACI, ConID, FunName, Args) of
{ok, UnsignedTX} -> {ok, UnsignedTX} ->
case Type of case Type of
call -> do_call(State, ConID, PK, UnsignedTX); call -> do_call(State, ConID, PK, UnsignedTX);
@@ -594,33 +593,39 @@ deploy2(State, Source) ->
case compile(Source) of case compile(Source) of
% Options = sophia_options(), % Options = sophia_options(),
% case so_compiler:from_string(Source, Options) of % case so_compiler:from_string(Source, Options) of
% TODO: Make hz accept either the aaci or the aci, preferring the aaci if present
{ok, Build} -> {ok, Build} ->
deploy3(State, Build); ACI = maps:get(aci, Build),
RawAACI = {aaci, ContractName, Funs, NS} = hz:prepare_aaci(ACI),
{InitSpec, Callable} = maps:take("init", Funs),
AACI = setelement(3, RawAACI, Callable),
Complete = maps:put(aaci, AACI, Build),
ok = tell(info, "Deploying Contract: ~p", [ContractName]),
deploy3(State, InitSpec, Complete, NS);
Other -> Other ->
ok = tell(info, "Compilation Failed!~n~tp", [Other]), ok = tell(info, "Compilation Failed!~n~tp", [Other]),
State State
end. end.
deploy3(State, Build) -> deploy3(State, InitSpec, Build, NS) ->
case gd_con:list_keys() of case gd_con:list_keys() of
{ok, 0, []} -> {ok, 0, []} ->
handle_troubling(State, "No keys exist in the current wallet."); handle_troubling(State, "No keys exist in the current wallet.");
{ok, Selected, Keys} -> {ok, Selected, Keys} ->
deploy4(State, Build, Selected, Keys); deploy4(State, InitSpec, Build, NS, Selected, Keys);
error -> error ->
handle_troubling(State, "No wallet is selected!") handle_troubling(State, "No wallet is selected!")
end. end.
deploy4(State = #s{frame = Frame, j = J}, Build = #{aci := ACI}, Selected, Keys) -> deploy4(State = #s{frame = Frame, j = J}, InitSpec, Build, NS, Selected, Keys) ->
{#{functions := Funs}, _} = find_main(ACI), InitArgs = element(1, InitSpec),
#{arguments := As} = lom:find(name, <<"init">>, Funs),
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Deploy Contract")), Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Deploy Contract")),
Sizer = wxBoxSizer:new(?wxVERTICAL), Sizer = wxBoxSizer:new(?wxVERTICAL),
ScrollWin = wxScrolledWindow:new(Dialog), ScrollWin = wxScrolledWindow:new(Dialog),
ScrollSz = wxBoxSizer:new(?wxVERTICAL), ScrollSz = wxBoxSizer:new(?wxVERTICAL),
ok = wxScrolledWindow:setSizerAndFit(ScrollWin, ScrollSz), ok = wxScrolledWindow:setSizerAndFit(ScrollWin, ScrollSz),
ok = wxScrolledWindow:setScrollRate(ScrollWin, 5, 5), ok = wxScrolledWindow:setScrollRate(ScrollWin, 5, 5),
FunName = unicode:characters_to_list(["init/", integer_to_list(length(As))]), FunName = unicode:characters_to_list(["init/", integer_to_list(length(InitArgs))]),
FunSz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, FunName}]), FunSz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, FunName}]),
KeySz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, J("Signature Key")}]), KeySz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, J("Signature Key")}]),
KeyPicker = wxChoice:new(ScrollWin, ?wxID_ANY, [{choices, Keys}]), KeyPicker = wxChoice:new(ScrollWin, ?wxID_ANY, [{choices, Keys}]),
@@ -635,24 +640,7 @@ deploy4(State = #s{frame = Frame, j = J}, Build = #{aci := ACI}, Selected, Keys)
GridSz = wxFlexGridSizer:new(2, 4, 4), GridSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL), ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1), ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
MakeArgField = ArgFields = make_arg_fields(ScrollWin, GridSz, InitArgs, NS, J),
fun(#{name := AN, type := T}) ->
Type =
case T of
<<"address">> -> address;
<<"int">> -> integer;
<<"bool">> -> boolean;
L when is_list(L) -> list; % FIXME
% I when is_binary(I) -> iface % FIXME
I when is_binary(I) -> address % FIXME
end,
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
TCT = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
_ = wxFlexGridSizer:add(GridSz, ANT, zxw:flags(base)),
_ = wxFlexGridSizer:add(GridSz, TCT, zxw:flags(wide)),
{ANT, TCT, Type}
end,
ArgFields = lists:map(MakeArgField, As),
_ = wxStaticBoxSizer:add(FunSz, GridSz, zxw:flags(wide)), _ = wxStaticBoxSizer:add(FunSz, GridSz, zxw:flags(wide)),
_ = wxStaticBoxSizer:add(ScrollSz, FunSz, [{proportion, 1}, {flag, ?wxEXPAND}]), _ = wxStaticBoxSizer:add(ScrollSz, FunSz, [{proportion, 1}, {flag, ?wxEXPAND}]),
_ = wxStaticBoxSizer:add(ScrollSz, KeySz, [{proportion, 0}, {flag, ?wxEXPAND}]), _ = wxStaticBoxSizer:add(ScrollSz, KeySz, [{proportion, 0}, {flag, ?wxEXPAND}]),
@@ -668,7 +656,7 @@ deploy4(State = #s{frame = Frame, j = J}, Build = #{aci := ACI}, Selected, Keys)
?wxID_OK -> ?wxID_OK ->
ID = wxChoice:getString(KeyPicker, wxChoice:getSelection(KeyPicker)), ID = wxChoice:getString(KeyPicker, wxChoice:getSelection(KeyPicker)),
PK = unicode:characters_to_binary(ID), PK = unicode:characters_to_binary(ID),
InitArgs = lists:map(fun get_arg/1, ArgFields), IArgs = lists:map(fun get_arg/1, ArgFields),
Controls = Controls =
[{"TTL", TTL_Tx}, [{"TTL", TTL_Tx},
{"Gas Price", GasP_Tx}, {"Gas Price", GasP_Tx},
@@ -678,7 +666,7 @@ deploy4(State = #s{frame = Frame, j = J}, Build = #{aci := ACI}, Selected, Keys)
{ok, [TTL, GasP, Gas, Amount]} -> {ok, [TTL, GasP, Gas, Amount]} ->
{ok, Nonce} = hz:next_nonce(PK), {ok, Nonce} = hz:next_nonce(PK),
DeployParams = {PK, Nonce, TTL, GasP, Gas, Amount}, DeployParams = {PK, Nonce, TTL, GasP, Gas, Amount},
{ok, DeployParams, InitArgs}; {ok, DeployParams, IArgs};
E -> E ->
E E
end; end;
@@ -844,9 +832,9 @@ open_hash3(State, Address, Source) ->
% TODO: Compile on load and verify the deployed hash for validity. % TODO: Compile on load and verify the deployed hash for validity.
Options = sophia_options(), Options = sophia_options(),
case so_compiler:from_string(Source, Options) of case so_compiler:from_string(Source, Options) of
{ok, Build = #{aci := ACI}} -> {ok, Build = #{aaci := AACI}} ->
{Defs = #{functions := Funs}, ConIfaces} = find_main(ACI), {Defs = #{functions := Funs}, ConIfaces} = find_main(AACI),
Callable = lom:delete(name, <<"init">>, Funs), Callable = maps:remove("init", Funs),
FunDefs = {maps:put(functions, Callable, Defs), ConIfaces}, FunDefs = {maps:put(functions, Callable, Defs), ConIfaces},
ok = tell(info, "Compilation Succeeded!~n~tp~n~n~tp", [Build, FunDefs]), ok = tell(info, "Compilation Succeeded!~n~tp~n~n~tp", [Build, FunDefs]),
add_code_page(State, {hash, Address}, Source); add_code_page(State, {hash, Address}, Source);
@@ -1100,12 +1088,16 @@ load3(State = #s{tabs = TopBook, cons = {Consbook, Pages}, buttons = Buttons, j
_ = wxSizer:add(PageSz, ConsSz, [{proportion, 1}, {flag, ?wxEXPAND}]), _ = wxSizer:add(PageSz, ConsSz, [{proportion, 1}, {flag, ?wxEXPAND}]),
{Out, IFaces, Build, NewButtons} = {Out, IFaces, Build, NewButtons} =
case compile(Source) of case compile(Source) of
{ok, B = #{aci := ACI}} -> {ok, Output} ->
{#{functions := Fs}, _} = find_main(ACI), ACI = maps:get(aci, Output),
Callable = lom:delete(name, <<"init">>, Fs), AACI = {aaci, ContractName, Funs, NS} = hz:prepare_aaci(ACI),
{NB, IFs} = fun_interfaces(ScrollWin, FunSz, Buttons, Callable, J), Complete = maps:put(aaci, AACI, Output),
O = io_lib:format("Compilation Succeeded!~n~tp~n~nDone!~n", [B]), ok = tell(info, "Loading Contract: ~p", [ContractName]),
{O, IFs, B, NB}; Callable = maps:remove("init", Funs),
tell(info, "Callable: ~p", [Callable]),
{NB, IFs} = fun_interfaces(ScrollWin, FunSz, Buttons, Callable, NS, J),
O = io_lib:format("Compilation Succeeded!~n~tp~n~nDone!~n", [Complete]),
{O, IFs, Complete, NB};
Other -> Other ->
O = io_lib:format("Compilation Failed!~n~tp~n", [Other]), O = io_lib:format("Compilation Failed!~n~tp~n", [Other]),
{O, [], none, Buttons} {O, [], none, Buttons}
@@ -1123,9 +1115,18 @@ load3(State = #s{tabs = TopBook, cons = {Consbook, Pages}, buttons = Buttons, j
State#s{cons = {Consbook, NewPages}, buttons = NewButtons}. State#s{cons = {Consbook, NewPages}, buttons = NewButtons}.
get_arg({_, TextCtrl, _}) -> get_arg({list, AFs, _}) ->
lists:map(fun get_arg/1, AFs);
get_arg({record, AFs}) ->
get_record(AFs);
get_arg({tuple, AFs}) ->
list_to_tuple(lists:map(fun get_arg/1, AFs));
get_arg({_, TextCtrl}) ->
wxTextCtrl:getValue(TextCtrl). wxTextCtrl:getValue(TextCtrl).
get_record([{L, A} | T]) ->
[{L, get_arg(A)} | get_record(T)].
find_main(ACI) -> find_main(ACI) ->
find_main(ACI, none, []). find_main(ACI, none, []).
@@ -1141,60 +1142,166 @@ find_main([C | T], M, Is) ->
find_main([], M, Is) -> find_main([], M, Is) ->
{M, Is}. {M, Is}.
fun_interfaces(ScrollWin, FunSz, Buttons, Funs, J) -> fun_interfaces(ScrollWin, FunSz, Buttons, Funs, NS, J) ->
MakeIface = MakeIface =
fun(#{name := N, arguments := As}) -> fun(Name, {Args, _}) ->
FunName = unicode:characters_to_list([N, "/", integer_to_list(length(As))]), tell(info, "Fun: ~p, Args: ~p", [Name, Args]),
FunName = unicode:characters_to_list([Name, "/", integer_to_list(length(Args))]),
FN = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, FunName}]), FN = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, FunName}]),
GridSz = wxFlexGridSizer:new(2, 4, 4), GridSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL), ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1), ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
MakeArgField = ArgFields = make_arg_fields(ScrollWin, GridSz, Args, NS, J),
fun(#{name := AN, type := T}) ->
Type =
case T of
<<"address">> -> address;
<<"int">> -> integer;
<<"bool">> -> boolean;
L when is_list(L) -> list; % FIXME
% I when is_binary(I) -> iface % FIXME
I when is_binary(I) -> address % FIXME
end,
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
TCT = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
_ = wxFlexGridSizer:add(GridSz, ANT, zxw:flags(base)),
_ = wxFlexGridSizer:add(GridSz, TCT, zxw:flags(wide)),
{ANT, TCT, Type}
end,
ArgFields = lists:map(MakeArgField, As),
ButtSz = wxBoxSizer:new(?wxHORIZONTAL), ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
{CallButton, DryRunButton} =
case N =:= <<"init">> of
false ->
CallBn = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Call")}]), CallBn = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Call")}]),
DryRBn = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Dry Run")}]), DryRBn = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Dry Run")}]),
_ = wxBoxSizer:add(ButtSz, CallBn, zxw:flags(wide)), _ = wxBoxSizer:add(ButtSz, CallBn, zxw:flags(wide)),
_ = wxBoxSizer:add(ButtSz, DryRBn, zxw:flags(wide)), _ = wxBoxSizer:add(ButtSz, DryRBn, zxw:flags(wide)),
{#w{name = {N, call}, id = wxButton:getId(CallBn), wx = CallBn}, CallButton = #w{name = {Name, call}, id = wxButton:getId(CallBn), wx = CallBn},
#w{name = {N, dryr}, id = wxButton:getId(DryRBn), wx = DryRBn}}; DryRButton = #w{name = {Name, dryr}, id = wxButton:getId(DryRBn), wx = DryRBn},
true ->
Deploy = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Deploy")}]),
_ = wxBoxSizer:add(ButtSz, Deploy, zxw:flags(wide)),
{#w{name = {N, call}, id = wxButton:getId(Deploy), wx = Deploy},
none}
end,
_ = wxStaticBoxSizer:add(FN, GridSz, zxw:flags(wide)), _ = wxStaticBoxSizer:add(FN, GridSz, zxw:flags(wide)),
_ = wxStaticBoxSizer:add(FN, ButtSz, zxw:flags(base)), _ = wxStaticBoxSizer:add(FN, ButtSz, zxw:flags(base)),
_ = wxSizer:add(FunSz, FN, zxw:flags(base)), _ = wxSizer:add(FunSz, FN, zxw:flags(base)),
#f{name = N, call = CallButton, dryrun = DryRunButton, args = ArgFields} #f{name = Name, call = CallButton, dryrun = DryRButton, args = ArgFields}
end, end,
IFaces = lists:map(MakeIface, Funs), IFaces = maps:map(MakeIface, Funs),
NewButtons = lists:foldl(fun map_iface_buttons/2, Buttons, IFaces), tell(info, "IFaces: ~p", [IFaces]),
NewButtons = maps:fold(fun map_iface_buttons/3, Buttons, IFaces),
{NewButtons, IFaces}. {NewButtons, IFaces}.
map_iface_buttons(#f{call = C = #w{id = CID}, dryrun = D = #w{id = DID}}, A) -> % FIXME: This can be simplified and needs to provide better widgets for types.
maps:put(DID, D, maps:put(CID, C, A)); % "variant" types should be wxChoice
map_iface_buttons(#f{call = C = #w{id = CID}, dryrun = none}, A) -> % Booleans should either be wxChoice or check boxes
% The sizer expansion direction for vertical elements is stupid
make_arg_fields(ScrollWin, GridSz, Args, NS, J) ->
MakeArgField =
fun
({AN, {T, already_normalized, T}}) ->
% tell(info, "~p Arg: ~p, Type: ~p", [?LINE, AN, T]),
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
TCT = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
_ = wxFlexGridSizer:add(GridSz, TCT, fill()),
{T, TCT};
({T, already_normalized, T}) ->
% tell(info, "~p Type: ~p", [?LINE, T]),
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, atom_to_list(T)),
TCT = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
_ = wxFlexGridSizer:add(GridSz, TCT, fill()),
{T, TCT};
({AN, {_TypeName, T, T}}) ->
% tell(info, "~p Arg: ~p, ~p: ~p", [?LINE, AN, TypeName, T]),
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
TCT = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
_ = wxFlexGridSizer:add(GridSz, TCT, fill()),
{T, TCT};
({AN, {_TypeName, already_normalized, {record, InnerArgs}}}) ->
% tell(info, "~p Arg: ~p, ~p: ~p", [?LINE, AN, TypeName, InnerArgs]),
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
InnerSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
_ = wxFlexGridSizer:add(GridSz, InnerSz, fill()),
{record, AFs};
({AN, {_TypeName, already_normalized, {tuple, InnerArgs}}}) ->
% tell(info, "~p Arg: ~p, ~p: ~p", [?LINE, AN, TypeName, InnerArgs]),
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
InnerSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
_ = wxFlexGridSizer:add(GridSz, InnerSz, fill()),
{tuple, AFs};
({AN, {_TypeName, already_normalized, {list, InnerArgs}}}) ->
% tell(info, "~p Arg: ~p, ~p: ~p", [?LINE, AN, TypeName, InnerArgs]),
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
ArgSz = wxBoxSizer:new(?wxVERTICAL),
InnerSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
B = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Add")}]),
AB = #w{name = {AN, add}, id = wxButton:getId(B), wx = B},
_ = wxBoxSizer:add(ArgSz, InnerSz, fill()),
_ = wxBoxSizer:add(ArgSz, B, fill()),
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
_ = wxFlexGridSizer:add(GridSz, ArgSz, fill()),
{list, AFs, AB};
({AN, {_TypeName, already_normalized, T}}) ->
% tell(info, "~p Arg: ~p, ~p: ~p", [?LINE, AN, TypeName, T]),
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
InnerSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
AFs = make_arg_fields(ScrollWin, InnerSz, T, NS, J),
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
_ = wxFlexGridSizer:add(GridSz, InnerSz, fill()),
{tuple, AFs};
({AN, {{tuple, _}, already_normalized, {tuple, InnerArgs}}}) ->
% tell(info, "~p Arg: ~p, Tuple: ~p", [?LINE, AN, InnerArgs]),
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
InnerSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
_ = wxFlexGridSizer:add(GridSz, InnerSz, fill()),
{tuple, AFs};
({AN, {{list, _}, already_normalized, {list, InnerArgs}}}) ->
% tell(info, "~p Arg: ~p, List: ~p", [?LINE, AN, InnerArgs]),
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
ArgSz = wxBoxSizer:new(?wxHORIZONTAL),
InnerSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
B = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Add")}]),
AB = #w{name = {AN, add}, id = wxButton:getId(B), wx = B},
_ = wxBoxSizer:add(ArgSz, InnerSz, fill()),
_ = wxBoxSizer:add(ArgSz, B, fill()),
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
_ = wxFlexGridSizer:add(GridSz, ArgSz, fill()),
{list, AFs, AB};
({{tuple, _}, already_normalized, {tuple, InnerArgs}}) ->
% tell(info, "~p Tuple: ~p", [?LINE, InnerArgs]),
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, "tuple"),
InnerSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
_ = wxFlexGridSizer:add(GridSz, InnerSz, fill()),
{tuple, AFs};
({{list, _}, already_normalized, {list, InnerArgs}}) ->
% tell(info, "~p List: ~p", [?LINE, InnerArgs]),
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, "list"),
ArgSz = wxBoxSizer:new(?wxHORIZONTAL),
InnerSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(InnerSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(InnerSz, 1),
AFs = make_arg_fields(ScrollWin, InnerSz, InnerArgs, NS, J),
B = wxButton:new(ScrollWin, ?wxID_ANY, [{label, J("Add")}]),
AB = #w{name = {list, add}, id = wxButton:getId(B), wx = B},
_ = wxBoxSizer:add(ArgSz, InnerSz, [{proportion, 1}, {flag, ?wxEXPAND}]),
_ = wxBoxSizer:add(ArgSz, B, fill()),
_ = wxFlexGridSizer:add(GridSz, ANT, fill()),
_ = wxFlexGridSizer:add(GridSz, ArgSz, fill()),
{list, AFs, AB}
end,
lists:map(MakeArgField, Args).
fill() ->
[{proportion, 0}, {flag, ?wxEXPAND}].
map_iface_buttons(_, #f{call = C = #w{id = CID}, dryrun = D = #w{id = DID}}, A) ->
maps:merge(#{CID => C, DID => D}, A);
map_iface_buttons(_, #f{call = C = #w{id = CID}, dryrun = none}, A) ->
maps:put(CID, C, A). maps:put(CID, C, A).