This commit is contained in:
Craig Everett 2026-02-25 16:21:56 +09:00
parent fcf44f55a1
commit ae17d21f4f
2 changed files with 233 additions and 147 deletions

226
src/gd_v_call.erl Normal file
View File

@ -0,0 +1,226 @@
-module(gd_v_devman).
-vsn("0.8.1").
-author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later").
-behavior(wx_object).
%-behavior(gd_v).
-include_lib("wx/include/wx.hrl").
-export([to_front/1]).
% -export([]).
-export([start_link/1]).
-export([init/1, terminate/2, code_change/3,
handle_call/3, handle_cast/2, handle_info/2, handle_event/2]).
-include("$zx_include/zx_logger.hrl").
-include("gd.hrl").
% Widgets
-record(w,
{name = none :: atom() | {FunName :: binary(), call | dryr},
id = 0 :: integer(),
wx = none :: none | wx:wx_object()}).
% State
-record(s,
{wx = none :: none | wx:wx_object(),
frame = none :: none | wx:wx_object(),
lang = en :: en | jp,
j = none :: none | fun(),
args = [] :: [#wx{}],
ttl = #wx{} :: #wx{},
gasprice = #wx{} :: #wx{},
gas = #wx{} :: #wx{},
amount = #wx{} :: #wx{},
retry = #wx{} :: #wx{},
copy = #wx{} :: #wx{},
done = #wx{} :: #wx{}}).
%%% Startup Functions
start_link(Args) ->
wx_object:start_link({local, ?MODULE}, ?MODULE, Args, []).
init({Prefs, {FunName, FunType}, ConID, AACI}) ->
{aaci, ConName, FunSpecs, _} = AACI,
FunSpec = maps:get(FunName, FunSpecs),
CallType =
case FunType of
call -> J("Contract Call");
dryr -> J("Dry Run")
end,
Title = [CallType, ": ", ConName, ".", FunName, "/", integer_to_list(length(element(1, FunSpec)))],
Lang = maps:get(lang, Prefs, en_us),
Trans = gd_jt:read_translations(?MODULE),
J = gd_jt:j(Lang, Trans),
Wx = wx:new(),
Frame = wxFrame:new(Wx, ?wxID_ANY, Title),
{ArgSz, Args} = call_arg_sizer(Frame, J, FunSpec),
{ParamSz, TTL_T, GasPriceT, GasT, AmountT} = call_param_sizer(Frame, J),
_ = wxFrame:setSizer(Frame, MainSz),
_ = wxSizer:layout(MainSz),
ok = gd_v:safe_size(Frame, Prefs),
ok = wxFrame:connect(Frame, close_window),
ok = wxFrame:connect(Frame, command_button_clicked),
% TODO: Decide where to put the frame -- should probably be slightly randomized.
% ok = wxFrame:center(Frame),
true = wxFrame:show(Frame),
State =
#s{wx = Wx, frame = Frame, j = J, prefs = Prefs,
args = Args,
ttl = TTL_T, gasprice = GasPriceT, gas = GasT, amount = AmountT,
retry = RetryBn, copy = CopyBn, done = DoneBn},
{Frame, State}.
call_arg_sizer(Frame, J, {CallArgs, ReturnType}) ->
SpecSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Function Spec")}]),
{CallSz, CallControls, Dimensions} = call_sizer(Frame, J, CallArgs),
ReturnSz = return_sizer(Frame, J, ReturnType),
_ = wxStaticBoxSizer:add(SpecSz, CallSz, zxw:flags({wide, 5})),
_ = wxStaticBoxSizer:add(SpecSz, ReturnSz, zxw:flags({base, 5})),
{SpecSz, CallControls, Dimensions}.
call_sizer(Frame, J, []) ->
CallSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Call Args")}]),
Args = wxStaticText:new(Frame, ?wxID_ANY, ["[", J("No Args"), "]"]),
_ = wxStaticBoxSizer:add(CallSz, Args, zxw:flags({wide, 5})),
{CallSz, [], {500, 500}};
call_sizer(Frame, J, CallArgs) ->
ScrollWin = wxScrolledWindow:new(Frame),
ScrollSz = wxBoxSizer:new(?wxVERTICAL),
ok = wxScrolledWindow:setSizerAndFit(ScrollWin, ScrollSz),
ok = wxScrolledWindow:setScrollRate(ScrollWin, 5, 5),
CallSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Call Args")}]),
GridSz = wxFlexGridSizer:new(2, [{gap, {4, 4}}]),
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
AddArg =
fun({Name, Type}) ->
L = wxStaticText:new(ScrollWin, ?wxID_ANY, [Name, " : ", textify(Type)]),
C = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
_ = wxFlexGridSizer:add(GridSz, L, zxw:flags({base, 5})),
_ = wxFlexGridSizer:add(GridSz, C, zxw:flags({wide, 5})),
{Name, C}
end,
_ = wxStaticBoxSizer:add(ScrollSz, GridSz, zxw:flags(wide)),
_ = wxStaticBoxSizer:add(CallSz, ScrollWin, zxw:flags(wide)),
Controls = lists:map(AddArg, CallArgs),
{CallSz, Controls, {600, 700}}.
return_sizer(Frame, J, ReturnType) ->
ReturnSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("Return Type")}]),
Return = wxStaticText:new(Frame, ?wxID_ANY, textify(ReturnType)),
_ = wxStaticBoxSizer:add(ReturnSz, Return, zxw:flags({wide, 5})),
ReturnSz.
call_param_sizer(Frame, J) ->
{ok, Height} = hz:top_height(),
DefTTL = Height + 10000,
DefGasP = hz:min_gas_price(),
DefGas = 5000000,
DefAmount = 0,
ParamSz = wxStaticBoxSizer:new(?wxVERTICAL, Frame, [{label, J("TX Parameters")}]),
GridSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
TTL_L = wxStaticText:new(Frame, ?wxID_ANY, "TTL"),
TTL_Tx = wxTextCtrl:new(Frame, ?wxID_ANY),
ok = wxTextCtrl:setValue(TTL_Tx, integer_to_list(DefTTL)),
GasP_L = wxStaticText:new(Frame, ?wxID_ANY, J("Gas Price")),
GasP_Tx = wxTextCtrl:new(Frame, ?wxID_ANY),
ok = wxTextCtrl:setValue(GasP_Tx, integer_to_list(DefGasP)),
Gas_L = wxStaticText:new(Frame, ?wxID_ANY, J("Gas")),
Gas_Tx = wxTextCtrl:new(Frame, ?wxID_ANY),
ok = wxTextCtrl:setValue(Gas_Tx, integer_to_list(DefGas)),
Amount_L = wxStaticText:new(Frame, ?wxID_ANY, J("TX Amount")),
Amount_Tx = wxTextCtrl:new(Frame, ?wxID_ANY),
ok = wxTextCtrl:setValue(Amount_Tx, integer_to_list(DefAmount)),
_ = wxFlexGridSizer:add(GridSz, TTL_L, zxw:flags({base, 5})),
_ = wxFlexGridSizer:add(GridSz, TTL_Tx, zxw:flags({wide, 5})),
_ = wxFlexGridSizer:add(GridSz, GasP_L, zxw:flags({base, 5})),
_ = wxFlexGridSizer:add(GridSz, GasP_Tx, zxw:flags({wide, 5})),
_ = wxFlexGridSizer:add(GridSz, Gas_L, zxw:flags({base, 5})),
_ = wxFlexGridSizer:add(GridSz, Gas_Tx, zxw:flags({wide, 5})),
_ = wxFlexGridSizer:add(GridSz, Amount_L, zxw:flags({base, 5})),
_ = wxFlexGridSizer:add(GridSz, Amount_Tx, zxw:flags({wide, 5})),
_ = wxSizer:add(ParamSz, GridSz, zxw:flags(wide)),
{ParamSz, TTL_Tx, GasP_Tx, Gas_Tx, Amount_Tx}.
textify({integer, _, _}) -> "int";
textify({boolean, _, _}) -> "bool";
textify({{bytes, [I]}, _, _}) -> io_lib:format("bytes(~w)", [I]);
textify({{bytes, any}, _, _}) -> "bytes()";
textify({T, _, _}) when is_atom(T) -> atom_to_list(T);
textify({T, _, _}) when is_list(T) -> T;
textify({T, _, _}) -> io_lib:format("~tp", [T]).
extract_args(Controls) ->
extract_args(Controls, []).
extract_args([], Acc) ->
pack_args(Acc);
extract_args([{Name, Control} | T], Acc) ->
String = wxTextCtrl:getValue(Control),
extract_args(T, [{Name, String} | Acc]).
pack_args(Args) ->
pack_args(Args, [], []).
pack_args([], M, A) ->
Message = unicode:characters_to_list(["Call Args:\n", M]),
{Message, A};
pack_args([{N, S} | T], M, A) ->
pack_args(T, [[N, ": ", S, "\n"] | M], [S | A]).
call_params(Controls) ->
call_params(Controls, {[], []}).
call_params([], {Acc, []}) ->
{ok, lists:reverse(Acc)};
call_params([], {_, Errors}) ->
{error, lists:reverse(Errors)};
call_params([{{Label, Validate}, Control} | T], {Acc, Errors}) ->
String = wxTextCtrl:getValue(Control),
case Validate(String) of
{ok, Value} -> call_params(T, {[Value | Acc], Errors});
{error, Reason} -> call_params(T, {Acc, [{Label, Reason} | Errors]})
end.
gt_0(S) ->
C = "Must be an integer greater than 0",
R =
try
{ok, list_to_integer(S)}
catch
error:badarg -> {error, {S, C}}
end,
case R of
{ok, N} when N > 0 -> {ok, N};
{ok, N} when N =< 0 -> {error, {S, C}};
Error -> Error
end.
gte_0(S) ->
C = "Must be a non-negative integer.",
R =
try
{ok, list_to_integer(S)}
catch
error:badarg -> {error, {S, C}}
end,
case R of
{ok, N} when N >= 0 -> {ok, N};
{ok, N} when N < 0 -> {error, {S, C}};
Error -> Error
end.

View File

@ -322,7 +322,9 @@ clicked(State = #s{cons = {Consbook, Contracts}}, Name) ->
ok = tell(warning, "Inconcievable! No notebook page is selected!"), ok = tell(warning, "Inconcievable! No notebook page is selected!"),
State; State;
Index -> Index ->
Contract = lists:nth(Index + 1, Contracts), #c{id = ConID, build = #{aaci := AACI}} = lists:nth(Index + 1, Contracts),
clicked2(State, Contract, Name) clicked2(State, Contract, Name)
end. end.
@ -337,8 +339,10 @@ clicked2(State, Contract, Name) ->
end. end.
clicked3(State = #s{frame = Frame, j = J}, Contract, Name = {FunName, FunType}, Selected, Keys) -> clicked3(State = #s{frame = Frame, j = J}, Contract, Name = {FunName, FunType}, Selected, Keys) ->
{FunName, FunType} = Name,
#c{id = ConID, build = #{aaci := AACI}} = Contract, #c{id = ConID, build = #{aaci := AACI}} = Contract,
{FunDef, ConID, AACI}
{aaci, ConName, FunSpecs, _} = AACI, {aaci, ConName, FunSpecs, _} = AACI,
FunSpec = maps:get(FunName, FunSpecs), FunSpec = maps:get(FunName, FunSpecs),
tell("FunName: ~tp", [FunName]), tell("FunName: ~tp", [FunName]),
@ -400,149 +404,6 @@ clicked3(State = #s{frame = Frame, j = J}, Contract, Name = {FunName, FunType},
Error -> handle_troubling(State, Error) Error -> handle_troubling(State, Error)
end. end.
extract_args(Controls) ->
extract_args(Controls, []).
extract_args([], Acc) ->
pack_args(Acc);
extract_args([{Name, Control} | T], Acc) ->
String = wxTextCtrl:getValue(Control),
extract_args(T, [{Name, String} | Acc]).
pack_args(Args) ->
pack_args(Args, [], []).
pack_args([], M, A) ->
Message = unicode:characters_to_list(["Call Args:\n", M]),
{Message, A};
pack_args([{N, S} | T], M, A) ->
pack_args(T, [[N, ": ", S, "\n"] | M], [S | A]).
call_arg_sizer(Dialog, J, {CallArgs, ReturnType}) ->
SpecSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Function Spec")}]),
{CallSz, CallControls, Dimensions} = call_sizer(Dialog, J, CallArgs),
ReturnSz = return_sizer(Dialog, J, ReturnType),
_ = wxStaticBoxSizer:add(SpecSz, CallSz, zxw:flags({wide, 5})),
_ = wxStaticBoxSizer:add(SpecSz, ReturnSz, zxw:flags({base, 5})),
{SpecSz, CallControls, Dimensions}.
call_sizer(Dialog, J, []) ->
CallSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Call Args")}]),
Args = wxStaticText:new(Dialog, ?wxID_ANY, ["[", J("No Args"), "]"]),
_ = wxStaticBoxSizer:add(CallSz, Args, zxw:flags({wide, 5})),
{CallSz, [], {500, 500}};
call_sizer(Dialog, J, CallArgs) ->
ScrollWin = wxScrolledWindow:new(Dialog),
ScrollSz = wxBoxSizer:new(?wxVERTICAL),
ok = wxScrolledWindow:setSizerAndFit(ScrollWin, ScrollSz),
ok = wxScrolledWindow:setScrollRate(ScrollWin, 5, 5),
CallSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Call Args")}]),
GridSz = wxFlexGridSizer:new(2, [{gap, {4, 4}}]),
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
AddArg =
fun({Name, Type}) ->
L = wxStaticText:new(ScrollWin, ?wxID_ANY, [Name, " : ", textify(Type)]),
C = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
_ = wxFlexGridSizer:add(GridSz, L, zxw:flags({base, 5})),
_ = wxFlexGridSizer:add(GridSz, C, zxw:flags({wide, 5})),
{Name, C}
end,
_ = wxStaticBoxSizer:add(ScrollSz, GridSz, zxw:flags(wide)),
_ = wxStaticBoxSizer:add(CallSz, ScrollWin, zxw:flags(wide)),
Controls = lists:map(AddArg, CallArgs),
{CallSz, Controls, {600, 700}}.
return_sizer(Dialog, J, ReturnType) ->
ReturnSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Return Type")}]),
Return = wxStaticText:new(Dialog, ?wxID_ANY, textify(ReturnType)),
_ = wxStaticBoxSizer:add(ReturnSz, Return, zxw:flags({wide, 5})),
ReturnSz.
textify({integer, _, _}) -> "int";
textify({boolean, _, _}) -> "bool";
textify({{bytes, [I]}, _, _}) -> io_lib:format("bytes(~w)", [I]);
textify({{bytes, any}, _, _}) -> "bytes()";
textify({T, _, _}) when is_atom(T) -> atom_to_list(T);
textify({T, _, _}) when is_list(T) -> T;
textify({T, _, _}) -> io_lib:format("~tp", [T]).
call_param_sizer(Dialog, J) ->
{ok, Height} = hz:top_height(),
DefTTL = Height + 10000,
DefGasP = hz:min_gas_price(),
DefGas = 5000000,
DefAmount = 0,
ParamSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("TX Parameters")}]),
GridSz = wxFlexGridSizer:new(2, 4, 4),
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
TTL_L = wxStaticText:new(Dialog, ?wxID_ANY, "TTL"),
TTL_Tx = wxTextCtrl:new(Dialog, ?wxID_ANY),
ok = wxTextCtrl:setValue(TTL_Tx, integer_to_list(DefTTL)),
GasP_L = wxStaticText:new(Dialog, ?wxID_ANY, J("Gas Price")),
GasP_Tx = wxTextCtrl:new(Dialog, ?wxID_ANY),
ok = wxTextCtrl:setValue(GasP_Tx, integer_to_list(DefGasP)),
Gas_L = wxStaticText:new(Dialog, ?wxID_ANY, J("Gas")),
Gas_Tx = wxTextCtrl:new(Dialog, ?wxID_ANY),
ok = wxTextCtrl:setValue(Gas_Tx, integer_to_list(DefGas)),
Amount_L = wxStaticText:new(Dialog, ?wxID_ANY, J("TX Amount")),
Amount_Tx = wxTextCtrl:new(Dialog, ?wxID_ANY),
ok = wxTextCtrl:setValue(Amount_Tx, integer_to_list(DefAmount)),
_ = wxFlexGridSizer:add(GridSz, TTL_L, zxw:flags({base, 5})),
_ = wxFlexGridSizer:add(GridSz, TTL_Tx, zxw:flags({wide, 5})),
_ = wxFlexGridSizer:add(GridSz, GasP_L, zxw:flags({base, 5})),
_ = wxFlexGridSizer:add(GridSz, GasP_Tx, zxw:flags({wide, 5})),
_ = wxFlexGridSizer:add(GridSz, Gas_L, zxw:flags({base, 5})),
_ = wxFlexGridSizer:add(GridSz, Gas_Tx, zxw:flags({wide, 5})),
_ = wxFlexGridSizer:add(GridSz, Amount_L, zxw:flags({base, 5})),
_ = wxFlexGridSizer:add(GridSz, Amount_Tx, zxw:flags({wide, 5})),
_ = wxSizer:add(ParamSz, GridSz, zxw:flags(wide)),
{ParamSz, TTL_Tx, GasP_Tx, Gas_Tx, Amount_Tx}.
call_params(Controls) ->
call_params(Controls, {[], []}).
call_params([], {Acc, []}) ->
{ok, lists:reverse(Acc)};
call_params([], {_, Errors}) ->
{error, lists:reverse(Errors)};
call_params([{{Label, Validate}, Control} | T], {Acc, Errors}) ->
String = wxTextCtrl:getValue(Control),
case Validate(String) of
{ok, Value} -> call_params(T, {[Value | Acc], Errors});
{error, Reason} -> call_params(T, {Acc, [{Label, Reason} | Errors]})
end.
gt_0(S) ->
C = "Must be an integer greater than 0",
R =
try
{ok, list_to_integer(S)}
catch
error:badarg -> {error, {S, C}}
end,
case R of
{ok, N} when N > 0 -> {ok, N};
{ok, N} when N =< 0 -> {error, {S, C}};
Error -> Error
end.
gte_0(S) ->
C = "Must be a non-negative integer.",
R =
try
{ok, list_to_integer(S)}
catch
error:badarg -> {error, {S, C}}
end,
case R of
{ok, N} when N >= 0 -> {ok, N};
{ok, N} when N < 0 -> {error, {S, C}};
Error -> Error
end.
clicked4(State, clicked4(State,
#c{id = ConID, build = #{aaci := AACI}}, #c{id = ConID, build = #{aaci := AACI}},
@ -785,7 +646,6 @@ deploy4(State = #s{frame = Frame, j = J}, InitSpec, Build, Selected, Keys) ->
end. end.
deploy5(State, Build, Params, Args) -> deploy5(State, Build, Params, Args) ->
tell(info, "Build: ~p", [Build]),
ok = gd_con:deploy(Build, Params, Args), ok = gd_con:deploy(Build, Params, Args),
State. State.
@ -952,7 +812,7 @@ save(State = #s{prefs = Prefs, code = {Codebook, Pages}}) ->
Index -> Index ->
case lists:nth(Index + 1, Pages) of case lists:nth(Index + 1, Pages) of
#p{path = {file, Path}, code = Widget} -> #p{path = {file, Path}, code = Widget} ->
Source = wxStyledTextCtrl:getText(Widget), Source = gd_sophia_editor:get_text(Widget),
case filelib:ensure_dir(Path) of case filelib:ensure_dir(Path) of
ok -> ok ->
case file:write_file(Path, Source) of case file:write_file(Path, Source) of