%%%-------------------------------------------------------------------
%%% @author Jack Tang <[email protected]>
%%% @copyright (C) 2014, Jack Tang
%%% @doc
%%%
%%% Conf = [{height, 100}, {width, 200}, {stripe_rows, true}, {id_field, uid}],
%%% ListView = ux_list_view:new(Parent, Conf),
%%% ux_list_view:columns(ListView,
%%% [ #col_def{text = "第一列", data_index = uid, sortable = true, align = right},
%%% #col_def{text = "姓名", data_index = uname} ]),
%%% ux_list_view:tbar([Item1, {Item2, left}, {Item3, right}]),
%%% ux_list_view:bbar([Item1, Item2]),
%%% ux_list_view:reg_handler s([{E1, Fun1}, {E2, Fun2}]),
%%% ux_list_view:load_data(ListView, Data),
%%% ux_list_view:update(ListView).
%%%
%%% @end
%%% Created : 6 Nov 2014 by Jack Tang <[email protected]>
%%%-------------------------------------------------------------------
-module(ux_list_view).
-behaviour(wx_object).
%% API
-export([new/2,
columns/2,
tbar/2,
bbar/2]).
-export([reg_handlers/2,
load_data/2,
update/1,
clear/1,
destroy/1]).
-export([init/1,
terminate/2,
code_change/3,
handle_info/2,
handle_call/3,
handle_cast/2,
handle_event/2]).
-include_lib("wx/include/wx.hrl").
-include("wxclient.hrl").
-record(state, { parent,
panel,
config,
columns,
id_field,
header_pos,
list_view,
tbar,
bbar,
callbacks,
store }).
%%%===================================================================
%%% API
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%% ux_list_view:new(Parent, [{items, Items},{id, ?wxID_ANY},
%% {height, 550}, {width, 600},
%% {stripe_rows, true}, {style, ?wxLC_REPORT bor ?wxLC_SINGLE_SEL}]),
%% @spec
%% @end
%%--------------------------------------------------------------------
new(Parent, Config) ->
NConfig = tuple_list_utils:update({parent, Parent}, Config),
start(NConfig).
start(Config) ->
wx_object:start_link(?MODULE, Config, []).
tbar(This, Items) ->
wx_object:call(This, {tbar, Items}).
bbar(This, Items) ->
wx_object:call(This, {bbar, Items}).
reg_handlers(This, EventAndHandlers) ->
wx_object:call(This, {reg_handlers, EventAndHandlers}).
row_value(This, RowId) ->
wx_object:call(This, {row_value, RowId}).
%%--------------------------------------------------------------------
%% @doc
%% Columns =
%% [#col_def{text = "第1列", data_index = currency, sortable = false, align = right},
%% #col_def{text = "第2列", data_index = uid, sortable = false, align = right},
%% #col_def{text = "第3列", data_index = instrument_id, sortable = false, align = right}],
%% ux_list_view:columns(ListView, Columns),
%% @spec
%% @end
%%--------------------------------------------------------------------
columns(This, Columns) ->
wx_object:call(This, {columns, Columns}).
%%--------------------------------------------------------------------
%% @doc
%% ux_list_view:load_data(Report, Data),
%% @spec
%% @end
%%--------------------------------------------------------------------
load_data(This, Data) ->
wx_object:call(This, {load_data, Data}).
%%--------------------------------------------------------------------
%% @doc
%% @spec
%% @end
%%--------------------------------------------------------------------
update(This) ->
wx_object:call(This, update).
%%--------------------------------------------------------------------
%% @doc
%% @spec
%% @end
%%--------------------------------------------------------------------
clear(This) ->
wx_object:call(This, clear).
%%--------------------------------------------------------------------
%% @doc
%% @spec
%% @end
%%--------------------------------------------------------------------
destroy(This) ->
wx_object:call(This, destroy).
%%--------------------------------------------------------------------
%% @doc
%% @spec
%% @end
%%--------------------------------------------------------------------
init(Config) ->
wx:batch(fun() -> do_init(Config) end).
handle_event(#wx{id = Id, event = Event}, #state{store = Store} = State) ->
#state{callbacks = Callbacks, list_view = ListView} = State,
EventSrc = case Event of
#wxCommand{type = Type} -> {Id, Type};
#wxList{type = Type} -> {Id, Type};
_ -> not_supported
end,
case maps:get(EventSrc, Callbacks, undefined) of
undefined -> ok;
Fun ->
Selections = selections(ListView, Store),
Fun(Selections)
end,
{noreply, State}.
handle_call({tbar, TbarItems}, _From, #state{panel = Panel} = State) ->
Tbar = create_bar(Panel, TbarItems),
{reply, ok, State#state{tbar = Tbar}};
handle_call({bbar, BbarItems}, _From, #state{panel = Panel} = State) ->
Bbar = create_bar(Panel, BbarItems),
{reply, ok, State#state{bbar = Bbar}};
handle_call({row_value, RowId}, _From, #state{store = Store} = State) ->
Reply = maps:get(RowId, Store, undefined),
{reply, Reply, State};
handle_call({reg_handlers, EventHandlerMappings}, _From, #state{} = State) ->
Callbacks = lists:foldl(fun({Event, Fun}, Map) ->
maps:put(Event, Fun, Map)
end, #{}, EventHandlerMappings),
{reply, ok, State#state{callbacks = Callbacks}};
handle_call(update, _From, #state{panel = Panel} = State) ->
#state{tbar = Tbar, bbar = Bbar, list_view = ListView} = State,
MainSizer = wxBoxSizer:new(?wxVERTICAL),
case Tbar of
undefined -> ok;
_ -> wxSizer:add(MainSizer, Tbar, [{flag, ?wxEXPAND}])
end,
wxSizer:add(MainSizer, ListView, [{flag, ?wxEXPAND}, {proportion, 1}]),
case Bbar of
undefined -> ok;
_ -> wxSizer:add(MainSizer, Bbar, [{flag, ?wxEXPAND}])
end,
wxPanel:setSizer(Panel, MainSizer),
wxSizer:fit(MainSizer, Panel),
{reply, ok, State};
handle_call({columns, Columns}, _From, State) ->
#state{list_view = ListView, id_field = IdField} = State,
HeaderPos = set_headers(ListView, Columns, IdField),
{reply, ok, State#state{columns = Columns, header_pos = HeaderPos}};
handle_call({load_data, Data}, _From, #state{store = Store} = State) ->
#state{header_pos = HeaderPos, list_view = ListView,
id_field = IdField, store = Store} = State,
[HeaderPosMap, NStore] = insert_rows(ListView, HeaderPos, Data, IdField, Store),
{reply, ok, State#state{header_pos = HeaderPosMap, store = NStore}};
handle_call(clear, _From, #state{list_view = ListView } = State) ->
wxListCtrl:clearAll(ListView),
wxListCtrl:refresh(ListView),
{reply, ok, State#state{header_pos = undefined}};
handle_call(destroy, _From, #state{} = State) ->
{stop, normal, ok, State};
handle_call(_Req, _From, State) ->
lager:error("Can't handle request: ~p", [_Req]),
{reply, {error, invalid_request},State}.
handle_cast(_Msg, State) ->
lager:error("Can't handle msg: ~p", [_Msg]),
{noreply, State}.
handle_info(_Info, State) ->
lager:error("Can't handle info: ~p", [_Info]),
{noreply, State}.
code_change(_, _, State) ->
{stop, ignore, State}.
terminate(_Reason, _State) ->
ok.
%%%===================================================================
%%% Internal functions
%%%===================================================================
do_init(Config) ->
{_, Parent} = tuple_list_utils:keyfind(parent, wx:null(), Config),
{_, IdField} = tuple_list_utils:keyfind(id_field, id, Config),
Style = tuple_list_utils:keyfind(style, ?wxLC_REPORT, Config),
Panel = wxPanel:new(Parent, [{size, wxWindow:getSize(Parent)}, {style, ?wxEXPAND}]),
ListView = wxListCtrl:new(Panel, [Style]),
% add listener
wxListCtrl:connect(ListView, command_list_item_selected, []),
process_flag(trap_exit, true),
{Panel, #state{parent = Parent, config = Config,
id_field = IdField, panel = Panel,
list_view = ListView, store = maps:new()}}.
insert_rows(ListView, undefined, [FirstRow | _] = Data, IdField, Store) ->
HeaderPos =
case FirstRow of
{struct, RowData} when is_list(RowData)->
set_headers(ListView, RowData, IdField);
{obj, RowData} when is_list(RowData)->
set_headers(ListView, RowData, IdField);
Row when is_list(Row) ->
set_headers(ListView, Row, IdField);
_Row ->
#{}
end,
insert_rows(ListView, HeaderPos, Data, IdField, Store);
insert_rows(ListView, HeaderPos, Data, IdField, Store) ->
LastRowNo = wxListCtrl:getItemCount(ListView),
[_, NDStore2] =
lists:foldl(
fun(Row, [RIndex, DStore]) ->
% fill up cells
[RIndex2, RData] = case Row of
{struct, RowData} when is_list(RowData)->
insert_one_row(ListView, HeaderPos, RIndex, RowData),
[RIndex + 1, RowData];
{obj, RowData} when is_list(RowData)->
insert_one_row(ListView, HeaderPos, RIndex, RowData),
[RIndex + 1, RowData];
Row when is_list(Row) ->
insert_one_row(ListView, HeaderPos, RIndex, Row),
[RIndex + 1, Row];
_Row ->
[RIndex, undefined]
end,
NDStore = case RData of
undefined -> DStore;
_ ->
case tuple_list_utils:keyfind(IdField, RData) of
{_, undefined} -> DStore;
{_, IdVal} ->
IdValStr = type_utils:any_to_list(IdVal),
maps:put(IdValStr, RData, DStore)
end
end,
[RIndex2, NDStore]
end, [LastRowNo, Store], Data),
[HeaderPos, NDStore2].
create_bar(Panel, Items) ->
ItemNumber = length(lists:flatten(Items)),
LeftSizer = wxFlexGridSizer:new(1, ItemNumber, 0, 0),
RightSizer = wxFlexGridSizer:new(1, ItemNumber, 0, 0),
lists:foreach(
fun(Item) ->
Wx2 = case Item of
{Wx, left} ->
wxWindow:reparent(Wx, Panel),
wxSizer:add(LeftSizer, Wx, [{proportion, 0}, {flag, ?wxALIGN_CENTER}]),
Wx;
{Wx, right} ->
wxWindow:reparent(Wx, Panel),
wxSizer:add(RightSizer, Wx, [{proportion, 0}, {flag, ?wxALIGN_CENTER}]),
Wx;
Wx ->
wxWindow:reparent(Wx, Panel),
wxSizer:add(LeftSizer, Wx, [{proportion, 0}, {flag, ?wxALIGN_CENTER}]),
Wx
end,
wxWindow:connect(Wx2, command_button_clicked)
end, Items),
MainSizer = wxBoxSizer:new(?wxHORIZONTAL),
wxSizer:add(MainSizer, LeftSizer),
wxSizer:add(MainSizer, 60 ,20, [{proportion, 1}, {flag, ?wxEXPAND}]),
wxSizer:add(MainSizer, RightSizer),
MainSizer.
set_headers(Report, Columns, IdField) ->
HeaderPosMap = maps:new(),
% setup id cell
IdHeaderCell = set_header_cell([{width, 0}, {text, "ID"}]),
wxListCtrl:insertColumn(Report, 0, IdHeaderCell),
NHeaderPosMap = map_utils:append(IdField, [0], HeaderPosMap),
[HeaderPosMap2, _] =
lists:foldl(
fun(Column, [HeaderPos, Index]) ->
[HeaderCol, DIndex] =
case Column of
#col_def{} ->
#col_def{text = Text, data_index = DataIndex,
sortable = Sortable, align = Align,
width = Width} = Column,
% validate DataIndex, it should exist!
[set_header_cell([{align, Align}, {width, Width}, {text, Text}]),
DataIndex];
{Field, _} ->
[set_header_cell([{text, type_utils:any_to_list(Field)}]), Field]
end,
wxListCtrl:insertColumn(Report, Index, HeaderCol),
[map_utils:append(DIndex, [Index], HeaderPos), Index + 1]
end, [NHeaderPosMap, 1], Columns),
HeaderPosMap2.
set_header_cell(Props) ->
HeaderCol = wxListItem:new(),
{align, Align} = tuple_list_utils:keyfind(align, ?wxLIST_FORMAT_LEFT, Props),
{text, Text} = tuple_list_utils:keyfind(text, "", Props),
wxListItem:setAlign(HeaderCol, Align),
% case tuple_list_utils:keyfind(width, undefined, Props) of
case tuple_list_utils:keyfind(width, 50, Props) of
{_, undefined} ->
ok;
{_, Width} ->
wxListItem:setWidth(HeaderCol, Width)
end,
wxListItem:setText(HeaderCol, Text),
HeaderCol.
insert_one_row(ListView, HeaderPos, RIndex, RowData) ->
% wxListCtrl:insertItem(ListView, RIndex),
wxListCtrl:insertItem(ListView, RIndex, ""),
maps:fold(fun(DIndex, CIndexList, Acc) ->
lists:foreach(
fun(CIndex) ->
{_, Val} = tuple_list_utils:keyfind(DIndex, "N/A", RowData),
Cell = wxListItem:new(),
wxListItem:setId(Cell, RIndex),
wxListItem:setColumn(Cell, CIndex),
wxListItem:setText(Cell, type_utils:any_to_list(Val)),
wxListCtrl:setItem(ListView, Cell)
end, CIndexList)
end, [], HeaderPos).
selections(ListView, Store) ->
SelectedCount = wxListCtrl:getSelectedItemCount(ListView),
Cell = wxListItem:new(),
{Selections, _LastSelected} =
lists:foldl(
fun(_Seq, {Sel, NextItem}) ->
NextSelectedItem = wxListCtrl:getNextItem(ListView, NextItem,
[{state, ?wxLIST_STATE_SELECTED}]),
wxListItem:setId(Cell, NextSelectedItem),
wxListCtrl:getItem(ListView, Cell),
SelectedId = wxListItem:getText(Cell),
Selected = maps:get(SelectedId, Store, undefined),
{lists:append(Sel, [Selected]), NextSelectedItem}
end, {[], -1}, lists:seq(1, SelectedCount)),
Selections.
lis
猜你喜欢
转载自870720136.iteye.com/blog/2169331
今日推荐
周排行