diff options
author | Dan Gudmundsson <dgud@erlang.org> | 2021-07-06 10:03:59 +0200 |
---|---|---|
committer | Dan Gudmundsson <dgud@erlang.org> | 2021-08-23 14:09:46 +0200 |
commit | 2426440b31dd70c985e02345ec593ac980d63e05 (patch) | |
tree | ed0d48bf9cb30b73b968c7a7025f793aed26498a /lib/debugger | |
parent | 307cb5e8a99b2e3f6f337fe6e89cf6e4faea33f6 (diff) | |
download | erlang-2426440b31dd70c985e02345ec593ac980d63e05.tar.gz |
Print records
Format module records in bindings area and in evaluator area.
Diffstat (limited to 'lib/debugger')
-rw-r--r-- | lib/debugger/src/dbg_ieval.erl | 93 | ||||
-rw-r--r-- | lib/debugger/src/dbg_iload.erl | 3 | ||||
-rw-r--r-- | lib/debugger/src/dbg_iserver.erl | 8 | ||||
-rw-r--r-- | lib/debugger/src/dbg_wx_trace.erl | 41 | ||||
-rw-r--r-- | lib/debugger/src/dbg_wx_trace_win.erl | 117 |
5 files changed, 204 insertions, 58 deletions
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index f7e859f174..3185b1e9e3 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -87,12 +87,14 @@ exit_info(Int, AttPid, OrigPid, Reason, ExitInfo) -> %% Evalute a shell expression in the real process. %% Called (dbg_icmd) in response to a user request. %%-------------------------------------------------------------------- -eval_expr(Expr, Bs, Ieval) -> +eval_expr(Expr0, Bs, Ieval) -> %% Save current exit info ExitInfo = get(exit_info), Stacktrace = get(stacktrace), + Expr = expand_records(Expr0, Ieval#ieval.module), + %% Emulate a surrounding catch try debugged_cmd({eval,Expr,Bs}, Bs, Ieval) catch @@ -1760,3 +1762,92 @@ get_stacktrace() -> Stk when is_list(Stk) -> Stk end. + +%%% eval record exprs +%%% copied from stdlib/src/shell.erl + +expand_records(Expr, Mod) -> + try + expand_records_1(used_record_defs(Expr, Mod), Expr) + catch _:_Err:_ST -> + Expr + end. + +expand_records_1([], Expr) -> + Expr; +expand_records_1(UsedRecords, Expr) -> + A = erl_anno:new(1), + RecordDefs = [{attribute, A, record, + {Name, [{record_field,A,{atom,A,F}} || F <- Fields]} + } || {Name,Fields} <- UsedRecords], + Forms0 = RecordDefs ++ [{function,A,foo,0,[{clause,A,[],[],[Expr]}]}], + Forms = erl_expand_records:module(Forms0, [strict_record_tests]), + {function,A,foo,0,[{clause,A,[],[],[NE]}]} = lists:last(Forms), + NE. + +used_record_defs(E, Mod) -> + case mod_recs(Mod) of + [] -> []; + Recs0 -> + Recs = [{Name, Fields} || {{_,_,Name,_}, Fields} <- Recs0], + L0 = used_record_defs(E, maps:from_list(Recs), [], []), + L1 = lists:zip(L0, lists:seq(1, length(L0))), + L2 = lists:keysort(2, lists:ukeysort(1, L1)), + [R || {R, _} <- L2] + end. + +used_record_defs(E, Recs, Skip, Used) -> + case used_records(E) of + {name,Name,E1} -> + case lists:member(Name, Skip) of + true -> + used_record_defs(E1, Recs, Skip, Used); + false -> + case maps:get(Name, Recs, undefined) of + undefined -> + used_record_defs(E1, Recs, [Name|Skip], Used); + Fields -> + used_record_defs(E1, Recs, [Name|Skip], [{Name, Fields}|Used]) + end + end; + {expr,[E1 | Es]} -> + used_record_defs(Es, Recs, Skip, used_record_defs(E1, Recs, Skip, Used)); + _ -> + Used + end. + +mod_recs(Mod) -> + case db_ref(Mod) of + not_found -> + []; + ModDb -> + dbg_idb:match_object(ModDb, {{record, Mod, '_', '_'}, '_'}) + end. + +used_records({record_index,_,Name,F}) -> + {name, Name, F}; +used_records({record,_,Name,Is}) -> + {name, Name, Is}; +used_records({record_field,_,R,Name,F}) -> + {name, Name, [R | F]}; +used_records({record,_,R,Name,Ups}) -> + {name, Name, [R | Ups]}; +used_records({record_field,_,R,F}) -> % illegal + {expr, [R | F]}; +used_records({call,_,{atom,_,record},[A,{atom,_,Name}]}) -> + {name, Name, A}; +used_records({call,_,{atom,_,is_record},[A,{atom,_,Name}]}) -> + {name, Name, A}; +used_records({call,_,{remote,_,{atom,_,erlang},{atom,_,is_record}}, + [A,{atom,_,Name}]}) -> + {name, Name, A}; +used_records({call,_,{atom,_,record_info},[A,{atom,_,Name}]}) -> + {name, Name, A}; +used_records({call,A,{tuple,_,[M,F]},As}) -> + used_records({call,A,{remote,A,M,F},As}); +used_records({type,_,record,[{atom,_,Name}|Fs]}) -> + {name, Name, Fs}; +used_records(T) when is_tuple(T) -> + {expr, tuple_to_list(T)}; +used_records(E) -> + {expr, E}. diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index 0f8164dadf..e7b74370fc 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -145,7 +145,8 @@ store_forms([{function,_,Name,Arity,Cs0}|Fs], Mod, Db, #{exp:=Exp} = St) -> store_forms(Fs, Mod, Db, St); store_forms([{attribute,_,record,{Name,Defs}}|Fs], Mod, Db, St) -> NDefs = normalise_rec_fields(Defs), - dbg_idb:insert(Db, {Mod,record,Name}, NDefs), + Fields = [F || {record_field, _, {atom, _, F}, _} <- NDefs], + dbg_idb:insert(Db, {record,Mod,Name,length(Fields)}, Fields), Recs = maps:get(recs, St, #{}), store_forms(Fs, Mod, Db, St#{recs => Recs#{Name => NDefs}}); store_forms([{attribute,_,_Name,_Val}|Fs], Mod, Db, St) -> diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl index 3e959e8e30..a9d157afa9 100644 --- a/lib/debugger/src/dbg_iserver.erl +++ b/lib/debugger/src/dbg_iserver.erl @@ -233,6 +233,14 @@ handle_call({load, Mod, Src, Bin}, _From, State) -> {reply, {module, Mod}, State}; %% Module database +handle_call({get_module_db, Mod}, _From, State) -> + Db = State#state.db, + Reply = case ets:lookup(Db, {Mod, refs}) of + [] -> not_found; + [{{Mod, refs}, [ModDb|_ModDbs]}] -> + ModDb + end, + {reply, Reply, State}; handle_call({get_module_db, Mod, Pid}, _From, State) -> Db = State#state.db, Reply = case ets:lookup(Db, {Mod, refs}) of diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl index 210987d3e6..d1334705c6 100644 --- a/lib/debugger/src/dbg_wx_trace.erl +++ b/lib/debugger/src/dbg_wx_trace.erl @@ -312,7 +312,7 @@ gui_cmd('Where', State) -> {_Status, Mod, Line} = State#state.status, Win = gui_show_module(State#state.win, Mod, Line, State#state.cm, State#state.pid, break), - gui_update_bindings(State#state.win, State#state.meta), + gui_update_bindings(State#state.win, Mod, State#state.meta), gui_enable_updown(State#state.stack_trace, Stack), dbg_wx_trace_win:display(State#state.win,State#state.status), State#state{win=Win, cm=Mod, stack=Stack}; @@ -330,10 +330,7 @@ gui_cmd('Messages', State) -> lists:foldl( fun(Msg, N) -> Str1 = io_lib:format(" ~w:", [N]), - dbg_wx_trace_win:eval_output(State#state.win,Str1, bold), - Str2 = pretty(Msg, State), - Str3 = io_lib:format(" ~ts~n",[Str2]), - dbg_wx_trace_win:eval_output(State#state.win,Str3, normal), + dbg_wx_trace_win:eval_output(State#state.win,Str1, Msg, normal), N+1 end, 1, @@ -363,7 +360,7 @@ gui_cmd('Up', State) -> {New, {undefined,-1}, _Bs} -> % call from non-interpreted code Stack = {New, Max}, Win = dbg_wx_trace_win:show_no_code(State#state.win), - dbg_wx_trace_win:update_bindings(State#state.win,[]), + dbg_wx_trace_win:update_bindings(State#state.win,undefined,[]), gui_enable_updown(State#state.stack_trace, Stack), dbg_wx_trace_win:display(State#state.win,{New,null,null}), State#state{win=Win, cm=null, stack=Stack}; @@ -373,7 +370,7 @@ gui_cmd('Up', State) -> Win = gui_show_module(State#state.win, Mod, Line, State#state.cm, State#state.pid, where), - dbg_wx_trace_win:update_bindings(State#state.win,Bs), + dbg_wx_trace_win:update_bindings(State#state.win,Mod,Bs), gui_enable_updown(State#state.stack_trace, Stack), dbg_wx_trace_win:display(State#state.win,{New,Mod,Line}), State#state{win=Win, cm=Mod, stack=Stack}; @@ -387,7 +384,7 @@ gui_cmd('Down', State) -> {New, {undefined,-1}, _Bs} -> % call from non-interpreted code Stack = {New, Max}, Win = dbg_wx_trace_win:show_no_code(State#state.win), - dbg_wx_trace_win:update_bindings(State#state.win, []), + dbg_wx_trace_win:update_bindings(State#state.win,undefined,[]), gui_enable_updown(State#state.stack_trace, Stack), dbg_wx_trace_win:display(State#state.win, {New,null,null}), State#state{win=Win, cm=null, stack=Stack}; @@ -397,7 +394,7 @@ gui_cmd('Down', State) -> Win = gui_show_module(State#state.win, Mod, Line, State#state.cm, State#state.pid, where), - dbg_wx_trace_win:update_bindings(State#state.win, Bs), + dbg_wx_trace_win:update_bindings(State#state.win, Mod, Bs), gui_enable_updown(State#state.stack_trace, Stack), dbg_wx_trace_win:display(State#state.win, {New,Mod,Line}), State#state{win=Win, cm=Mod, stack=Stack}; @@ -511,7 +508,7 @@ gui_cmd({user_command, Cmd}, State) -> int:meta(State#state.meta, eval, Arg); true -> Str = "Commands not allowed", - dbg_wx_trace_win:eval_output(State#state.win, [$<,Str,10], normal) + dbg_wx_trace_win:eval_output(State#state.win, [$<,Str,10], bold) end, State; @@ -633,7 +630,7 @@ meta_cmd({exit_at, {Mod,Line}, Reason, Cur}, State) -> gui_enable_functions(exit), gui_enable_updown(State#state.stack_trace, Stack), gui_enable_btrace(State#state.trace, State#state.stack_trace), - gui_update_bindings(State#state.win, State#state.meta), + gui_update_bindings(State#state.win, Mod, State#state.meta), dbg_wx_trace_win:display(State#state.win, {exit, {Mod,Line}, Reason}), State#state{win=Win, cm=Mod,status={exit,{Mod,Line},Reason}, stack=Stack}; @@ -645,7 +642,7 @@ meta_cmd({break_at, Mod, Line, Cur}, State) -> gui_enable_functions(break), gui_enable_updown(State#state.stack_trace, Stack), gui_enable_btrace(State#state.trace, State#state.stack_trace), - gui_update_bindings(State#state.win, State#state.meta), + gui_update_bindings(State#state.win, Mod, State#state.meta), dbg_wx_trace_win:display(State#state.win, {break, Mod, Line}), State#state{win=Win, cm=Mod, status={break,Mod,Line}, stack=Stack}; meta_cmd({func_at, Mod, Line, Cur}, State) -> @@ -668,7 +665,7 @@ meta_cmd({wait_at, Mod, Line, Cur}, State) -> gui_enable_functions(wait_break), gui_enable_updown(State#state.stack_trace, Stack), gui_enable_btrace(State#state.trace, State#state.stack_trace), - gui_update_bindings(State#state.win, State#state.meta), + gui_update_bindings(State#state.win, Mod, State#state.meta), dbg_wx_trace_win:display(State#state.win, {wait, Mod, Line}), State#state{win=Win, cm=Mod, status={wait_break,Mod,Line}, stack=Stack}; @@ -677,14 +674,14 @@ meta_cmd({wait_after_at, Mod, Line, Sp}, State) -> meta_cmd(running, State) -> Win = dbg_wx_trace_win:unmark_line(State#state.win), gui_enable_functions(running), - dbg_wx_trace_win:update_bindings(State#state.win, []), + dbg_wx_trace_win:update_bindings(State#state.win, undefined, []), dbg_wx_trace_win:display(State#state.win, {running, State#state.cm}), State#state{win=Win, status={running,null,null}}; meta_cmd(idle, State) -> Win = dbg_wx_trace_win:show_no_code(State#state.win), gui_enable_functions(idle), - dbg_wx_trace_win:update_bindings(State#state.win, []), + dbg_wx_trace_win:update_bindings(State#state.win, undefined, []), dbg_wx_trace_win:display(State#state.win, idle), State#state{win=Win, status={idle,null,null}, cm=undefined}; @@ -713,17 +710,9 @@ meta_cmd({trace_output, StrFun}, State) -> %% Reply on a user command meta_cmd({eval_rsp, Res}, State) -> - Str = pretty(Res, State), - dbg_wx_trace_win:eval_output(State#state.win, [$<,Str,10], normal), + dbg_wx_trace_win:eval_output(State#state.win, [$<], Res, normal), State. -pretty(Term, State) -> - Strings = case State#state.strings of - [str_on] -> true; - [] -> false - end, - io_lib_pretty:print(Term,[{encoding,unicode},{strings,Strings}]). - %%==================================================================== %% GUI auxiliary functions %%==================================================================== @@ -841,9 +830,9 @@ gui_load_module(Win, Mod, _Pid) -> dbg_wx_trace_win:show_no_code(Win) end. -gui_update_bindings(Win,Meta) -> +gui_update_bindings(Win,Mod,Meta) -> Bs = int:meta(Meta, bindings, nostack), - dbg_wx_trace_win:update_bindings(Win,Bs). + dbg_wx_trace_win:update_bindings(Win,Mod,Bs). gui_enable_functions(Status) -> Enable = enable(Status), diff --git a/lib/debugger/src/dbg_wx_trace_win.erl b/lib/debugger/src/dbg_wx_trace_win.erl index f5e5fbc5bf..526e673807 100644 --- a/lib/debugger/src/dbg_wx_trace_win.erl +++ b/lib/debugger/src/dbg_wx_trace_win.erl @@ -23,7 +23,7 @@ %% External exports -export([init/0, stop/1]). --export([create_win/4, +-export([create_win/4, get_window/1, configure/2, enable/2, is_enabled/1, select/2, @@ -31,11 +31,11 @@ clear_breaks/1, clear_breaks/2, display/2, % Help messages is_shown/2, % Code area - show_code/3, show_no_code/1, remove_code/2, + show_code/3, show_no_code/1, remove_code/2, mark_line/3, unmark_line/1, select_line/2, selected_line/1, - eval_output/3, % Evaluator area - update_bindings/2, % Bindings area + eval_output/3, eval_output/4, eval_output/5, % Evaluator area + update_bindings/3, % Bindings area update_strings/1, trace_output/2, % Trace area handle_event/2 @@ -205,7 +205,7 @@ create_win(Parent, Title, Windows, Menus) -> put(window, Win), put(strings, [str_on]), Wi - end, + end, try wx:batch(Do) catch E:R -> @@ -558,29 +558,83 @@ selected_line(#winInfo{editor={_,Ed}}) -> %% Str = string() %% Face = normal | bold %%-------------------------------------------------------------------- -eval_output(#winInfo{eval=#sub{out=Log}}, Text, _Face) -> +eval_output(#winInfo{eval=#sub{out=Log}}, Text, bold) -> + Style = wxTextCtrl:getDefaultStyle(Log), + Font = wxTextAttr:getFont(Style), + wxFont:setWeight(Font, ?wxFONTWEIGHT_BOLD), + wxTextAttr:setFont(Style, Font), + wxTextCtrl:setDefaultStyle(Log, Style), + wxTextCtrl:appendText(Log, dbg_wx_win:to_string(Text)), + wxFont:setWeight(Font, ?wxFONTWEIGHT_NORMAL), + wxTextAttr:setFont(Style, Font), + wxTextCtrl:setDefaultStyle(Log, Style), + ok; +eval_output(#winInfo{eval=#sub{out=Log}}, Text, _Face) -> wxTextCtrl:appendText(Log, dbg_wx_win:to_string(Text)), ok. - + +%%-------------------------------------------------------------------- +%% eval_output(winInfo{}, Prefix, Term, [Mod,] Face) +%% Prefix = string() +%% Term = term to be formatted +%% Mod = module() | undefined for record formatting +%% Face = normal | bold +%%-------------------------------------------------------------------- +eval_output(Wi, Prefix, Term, Face) -> + {Mod,_Bs} = get(bindings), + eval_output(Wi, Prefix, Term, Mod, Face). + +eval_output(#winInfo{eval=#sub{out=Log}}=Wi, Prefix, Term, Mod, Face) -> + {CW, _, _, _ } = wxTextCtrl:getTextExtent(Log,"w"), + {W, _} = wxTextCtrl:getClientSize(Log), + LineLength = max(40, W div CW), + Column = string:length(Prefix), + ValStr = format_term(Term, Mod, LineLength-Column, Column, -1, -1), + eval_output(Wi, Prefix, bold), + eval_output(Wi, [ValStr, "\n"], Face), + ok. + %%-------------------------------------------------------------------- %% update_bindings(Bs) %% Bs = [{Var,Val}] %%-------------------------------------------------------------------- -update_bindings(#winInfo{bind=#sub{out=BA}}, Bs) -> +update_bindings(#winInfo{bind=#sub{out=BA}}, Mod, Bs) -> wxListCtrl:deleteAllItems(BA), wx:foldl(fun({Var,Val},Row) -> - wxListCtrl:insertItem(BA, Row, ""), + wxListCtrl:insertItem(BA, Row, ""), wxListCtrl:setItem(BA, Row, 0, dbg_wx_win:to_string(Var)), - Format = case get(strings) of - [] -> "~0ltP"; - [str_on] -> "~0tP" - end, - wxListCtrl:setItem(BA, Row, 1, dbg_wx_win:to_string(Format,[Val, 20])), + Str = format_term_line(Val, Mod), + wxListCtrl:setItem(BA, Row, 1, Str), Row+1 end, 0, Bs), - put(bindings,Bs), + put(bindings,{Mod,Bs}), ok. +format_term_line(Val, Mod) -> + format_term(Val, Mod, 0, 1, 20, 300). + +format_term(Val, Mod, LineLenght, Column, Depth, Limit) -> + RecFun = fun(Tag,NoFields) -> record_fields(Tag, NoFields, Mod) end, + UseStrings = case get(strings) of + [] -> false; + [str_on] -> true + end, + Opts = [{line_length,LineLenght}, {depth, Depth}, {chars_limit, Limit}, {column, Column}, + {strings, UseStrings}, {encoding, unicode}, {record_print_fun, RecFun}], + io_lib_pretty:print(Val, Opts). + +record_fields(Tag, NoFields, Mod) -> + case dbg_iserver:call({get_module_db, Mod}) of + not_found -> no; + ModDb -> + case dbg_idb:lookup(ModDb, {record, Mod, Tag, NoFields}) of + {ok, Value} -> + Value; + not_found -> + no + end + end. + update_strings(Strings) -> _ = put(strings, Strings), ok. @@ -856,26 +910,23 @@ handle_event(#wx{id=?EVAL_ENTRY, event=#wxCommand{type=command_text_enter}}, eval_output(Wi, "\n", normal), ignore; Cmd -> - eval_output(Wi, [$>, Cmd, 10], normal), + eval_output(Wi, [$>, Cmd, 10], bold), wxTextCtrl:setValue(TC,""), {user_command, Cmd} end; %% Bindings area handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Row}},Wi) -> - Bs = get(bindings), + {Mod,Bs} = get(bindings), {Var,Val} = lists:nth(Row+1, Bs), - Str = case get(strings) of - [] -> io_lib:format("< ~s = ~ltp~n", [Var, Val]); - [str_on] -> io_lib:format("< ~s = ~tp~n", [Var, Val]) - end, - eval_output(Wi, Str, bold), + Header = io_lib:format("< ~s = ", [Var]), + eval_output(Wi, Header, Val, Mod, normal), ignore; -handle_event(#wx{event=#wxList{type=command_list_item_activated, itemIndex=Row}},_Wi) -> - Bs = get(bindings), +handle_event(#wx{event=#wxList{type=command_list_item_activated, itemIndex=Row}},_Wi) -> + {_Mod,Bs} = get(bindings), Binding = lists:nth(Row+1, Bs), {edit, Binding}; - + handle_event(_GSEvent, _WinInfo) -> %%io:format("~p: unhandled ~p~n",[?MODULE, _GSEvent]), ignore. @@ -939,7 +990,7 @@ button_area(Parent) -> search_area(Parent) -> HSz = wxBoxSizer:new(?wxHORIZONTAL), - _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Find:"), + _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Find: "), [{flag,?wxALIGN_CENTER_VERTICAL}]), TC1 = wxTextCtrl:new(Parent, ?SEARCH_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), _ = wxSizer:add(HSz, TC1, [{proportion,3}, {flag, ?wxEXPAND}]), @@ -951,7 +1002,7 @@ search_area(Parent) -> Cbtn = wxCheckBox:new(Parent, ?wxID_ANY, "Match Case"), _ = wxSizer:add(HSz,Cbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]), _ = wxSizer:add(HSz, 15,15, [{proportion,1}, {flag, ?wxEXPAND}]), - _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Goto Line:"), + _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Goto Line: "), [{flag,?wxALIGN_CENTER_VERTICAL}]), TC2 = wxTextCtrl:new(Parent, ?GOTO_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), _ = wxSizer:add(HSz, TC2, [{proportion,0}, {flag, ?wxEXPAND}]), @@ -970,13 +1021,19 @@ eval_area(Parent) -> VSz = wxBoxSizer:new(?wxVERTICAL), HSz = wxBoxSizer:new(?wxHORIZONTAL), - _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Evaluator:"), + _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Evaluator: "), [{flag,?wxALIGN_CENTER_VERTICAL}]), TC = wxTextCtrl:new(Parent, ?EVAL_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), _ = wxSizer:add(HSz, TC, [{proportion,1}, {flag, ?wxEXPAND}]), _ = wxSizer:add(VSz, HSz, [{flag, ?wxEXPAND}]), - TL = wxTextCtrl:new(Parent, ?EVAL_LOG, [{style, ?wxTE_DONTWRAP bor - ?wxTE_MULTILINE bor ?wxTE_READONLY}]), + TL = wxTextCtrl:new(Parent, ?EVAL_LOG, [{style, + ?wxTE_DONTWRAP + bor ?wxTE_MULTILINE + bor ?wxTE_READONLY + bor ?wxTE_RICH2 + }]), + FixedFont = wxFont:new(10, ?wxFONTFAMILY_TELETYPE, ?wxNORMAL, ?wxNORMAL,[]), + wxWindow:setFont(TL, FixedFont), _ = wxSizer:add(VSz, TL, [{proportion,5}, {flag, ?wxEXPAND}]), wxTextCtrl:connect(TC, command_text_enter), |