1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2009-2016. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19 20-module(ex_canvas). 21 22-behaviour(wx_object). 23 24%% Client API 25-export([start/1]). 26 27%% wx_object callbacks 28-export([init/1, terminate/2, code_change/3, 29 handle_info/2, handle_call/3, handle_cast/2, handle_event/2, handle_sync_event/3]). 30 31-include_lib("wx/include/wx.hrl"). 32 33-record(state, 34 { 35 parent, 36 config, 37 canvas, 38 bitmap, 39 overlay, 40 pos 41 }). 42 43start(Config) -> 44 wx_object:start_link(?MODULE, Config, []). 45 46%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 47init(Config) -> 48 wx:batch(fun() -> do_init(Config) end). 49 50do_init(Config) -> 51 Parent = proplists:get_value(parent, Config), 52 Panel = wxPanel:new(Parent, []), 53 54 %% Setup sizers 55 MainSizer = wxBoxSizer:new(?wxVERTICAL), 56 Sizer = wxStaticBoxSizer:new(?wxVERTICAL, Panel, 57 [{label, "Various shapes"}]), 58 59 Button = wxButton:new(Panel, ?wxID_ANY, [{label, "Redraw"}]), 60 61 Canvas = wxPanel:new(Panel, [{style, ?wxFULL_REPAINT_ON_RESIZE}]), 62 63 wxPanel:connect(Canvas, paint, [callback]), 64 wxPanel:connect(Canvas, size), 65 wxPanel:connect(Canvas, left_down), 66 wxPanel:connect(Canvas, left_up), 67 wxPanel:connect(Canvas, motion), 68 69 wxPanel:connect(Button, command_button_clicked), 70 71 %% Add to sizers 72 wxSizer:add(Sizer, Button, [{border, 5}, {flag, ?wxALL}]), 73 wxSizer:addSpacer(Sizer, 5), 74 wxSizer:add(Sizer, Canvas, [{flag, ?wxEXPAND}, 75 {proportion, 1}]), 76 77 wxSizer:add(MainSizer, Sizer, [{flag, ?wxEXPAND}, 78 {proportion, 1}]), 79 80 wxPanel:setSizer(Panel, MainSizer), 81 wxSizer:layout(MainSizer), 82 83 {W,H} = wxPanel:getSize(Canvas), 84 Bitmap = wxBitmap:new(erlang:max(W,30),erlang:max(30,H)), 85 86 {Panel, #state{parent=Panel, config=Config, 87 canvas = Canvas, bitmap = Bitmap, 88 overlay = wxOverlay:new() 89 }}. 90 91%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 92%% Sync event from callback events, paint event must be handled in callbacks 93%% otherwise nothing will be drawn on windows. 94handle_sync_event(#wx{event = #wxPaint{}}, _wxObj, 95 #state{canvas=Canvas, bitmap=Bitmap}) -> 96 DC = wxPaintDC:new(Canvas), 97 redraw(DC, Bitmap), 98 wxPaintDC:destroy(DC), 99 ok. 100 101%% Async Events are handled in handle_event as in handle_info 102handle_event(#wx{event = #wxCommand{type = command_button_clicked}}, 103 State = #state{}) -> 104 Image = wxImage:new("image.jpg"), 105 Image2 = wxImage:scale(Image, wxImage:getWidth(Image) div 3, 106 wxImage:getHeight(Image) div 3), 107 Bmp = wxBitmap:new(Image2), 108 wxImage:destroy(Image), 109 wxImage:destroy(Image2), 110 {W,H} = wxPanel:getSize(State#state.canvas), 111 Positions = lists:map(fun(_) -> 112 get_pos(W,H) 113 end, lists:seq(1,(W+H) div 20)), 114 Fun = fun(DC) -> 115 wxDC:clear(DC), 116 lists:foreach(fun({X,Y}=Pos) -> 117 wxDC:setBrush(DC, ?wxTRANSPARENT_BRUSH), 118 wxDC:setPen(DC, wxPen:new(?wxBLACK, [{width, 2}])), 119 case X rem 6 of 120 0 -> wxDC:drawBitmap(DC, Bmp, Pos); 121 1 -> wxDC:setBrush(DC, ?wxRED_BRUSH), 122 wxDC:drawRectangle(DC, Pos, {20,20}); 123 2 -> wxDC:setBrush(DC, ?wxBLUE_BRUSH), 124 wxDC:drawCircle(DC, {X+10, Y+10}, 15); 125 3 -> wxDC:setPen(DC, wxPen:new({200,200,0,255}, [{width, 4}])), 126 wxDC:drawLine(DC, Pos, get_pos(W,H)); 127 4 -> wxDC:setBrush(DC, ?wxGREEN_BRUSH), 128 wxDC:drawEllipse(DC, Pos, {60,20}); 129 _ -> wxDC:drawLabel(DC, "Erlang /", {X,Y,60,20}), 130 wxDC:drawRotatedText(DC, "OTP", {X+60,Y}, 340.0) 131 end 132 end, Positions) 133 end, 134 draw(State#state.canvas, State#state.bitmap, Fun), 135 wxBitmap:destroy(Bmp), 136 {noreply, State}; 137handle_event(#wx{event = #wxSize{size={W,H}}}, 138 State = #state{bitmap=Prev, canvas=Canvas}) -> 139 if W > 0 andalso H > 0 -> 140 Bitmap = wxBitmap:new(W,H), 141 draw(Canvas, Bitmap, fun(DC) -> wxDC:clear(DC) end), 142 wxBitmap:destroy(Prev), 143 {noreply, State#state{bitmap = Bitmap}}; 144 true -> 145 {noreply, State} 146 end; 147handle_event(#wx{event = #wxMouse{type=left_down, x=X, y=Y}}, State) -> 148 {noreply, State#state{pos={X,Y}}}; 149handle_event(#wx{event = #wxMouse{type=motion, x=X1, y=Y1}}, 150 #state{pos=Start, overlay=Overlay, canvas=Canvas} = State) -> 151 case Start of 152 undefined -> ignore; 153 {X0,Y0} -> 154 DC = wxClientDC:new(Canvas), 155 DCO = wxDCOverlay:new(Overlay, DC), 156 wxDCOverlay:clear(DCO), 157 wxDC:setPen(DC, ?wxLIGHT_GREY_PEN), 158 wxDC:setBrush(DC, ?wxTRANSPARENT_BRUSH), 159 wxDC:drawRectangle(DC, {X0,Y0, X1-X0, Y1-Y0}), 160 wxDCOverlay:destroy(DCO), 161 wxClientDC:destroy(DC) 162 end, 163 {noreply, State}; 164handle_event(#wx{event = #wxMouse{type=left_up}}, 165 #state{overlay=Overlay, canvas=Canvas} = State) -> 166 DC = wxClientDC:new(Canvas), 167 DCO = wxDCOverlay:new(Overlay, DC), 168 wxDCOverlay:clear(DCO), 169 wxDCOverlay:destroy(DCO), 170 wxClientDC:destroy(DC), 171 wxOverlay:reset(Overlay), 172 {noreply, State#state{pos=undefined}}; 173 174handle_event(Ev = #wx{}, State = #state{}) -> 175 demo:format(State#state.config, "Got Event ~p\n", [Ev]), 176 {noreply, State}. 177 178%% Callbacks handled as normal gen_server callbacks 179handle_info(Msg, State) -> 180 demo:format(State#state.config, "Got Info ~p\n", [Msg]), 181 {noreply, State}. 182 183handle_call(shutdown, _From, State=#state{parent=Panel}) -> 184 wxPanel:destroy(Panel), 185 {stop, normal, ok, State}; 186handle_call(Msg, _From, State) -> 187 demo:format(State#state.config, "Got Call ~p\n", [Msg]), 188 {reply,{error, nyi}, State}. 189 190handle_cast(Msg, State) -> 191 io:format("Got cast ~p~n",[Msg]), 192 {noreply,State}. 193 194code_change(_, _, State) -> 195 {stop, ignore, State}. 196 197terminate(_Reason, #state{overlay=Overlay}) -> 198 wxOverlay:destroy(Overlay), 199 ok. 200 201%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 202%% Local functions 203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 204 205%% Buffered makes it all appear on the screen at the same time 206draw(Canvas, Bitmap, Fun) -> 207 MemoryDC = wxMemoryDC:new(Bitmap), 208 Fun(MemoryDC), 209 210 CDC = wxWindowDC:new(Canvas), 211 wxDC:blit(CDC, {0,0}, 212 {wxBitmap:getWidth(Bitmap), wxBitmap:getHeight(Bitmap)}, 213 MemoryDC, {0,0}), 214 wxWindowDC:destroy(CDC), 215 wxMemoryDC:destroy(MemoryDC). 216 217redraw(DC, Bitmap) -> 218 MemoryDC = wxMemoryDC:new(Bitmap), 219 wxDC:blit(DC, {0,0}, 220 {wxBitmap:getWidth(Bitmap), wxBitmap:getHeight(Bitmap)}, 221 MemoryDC, {0,0}), 222 wxMemoryDC:destroy(MemoryDC). 223 224get_pos(W,H) -> 225 {rand:uniform(W), rand:uniform(H)}. 226