]> git.saurik.com Git - wxWidgets.git/blob - src/univ/button.cpp
Glitches in wxDelegateRenderer
[wxWidgets.git] / src / univ / button.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: univ/button.cpp
3 // Purpose: wxButton
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 14.08.00
7 // RCS-ID: $Id$
8 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "univbutton.h"
22 #endif
23
24 #include "wx/wxprec.h"
25
26 #ifdef __BORLANDC__
27 #pragma hdrstop
28 #endif
29
30 #if wxUSE_BUTTON
31
32 #ifndef WX_PRECOMP
33 #include "wx/dcclient.h"
34 #include "wx/dcscreen.h"
35 #include "wx/button.h"
36 #include "wx/validate.h"
37 #endif
38
39 #include "wx/univ/renderer.h"
40 #include "wx/univ/inphand.h"
41 #include "wx/univ/theme.h"
42 #include "wx/univ/colschem.h"
43
44 // ----------------------------------------------------------------------------
45 // constants
46 // ----------------------------------------------------------------------------
47
48 // default margins around the image
49 static const wxCoord DEFAULT_BTN_MARGIN_X = 0; // We should give space for the border, at least.
50 static const wxCoord DEFAULT_BTN_MARGIN_Y = 0;
51
52 // ============================================================================
53 // implementation
54 // ============================================================================
55
56 IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl)
57
58 // ----------------------------------------------------------------------------
59 // creation
60 // ----------------------------------------------------------------------------
61
62 void wxButton::Init()
63 {
64 m_isPressed =
65 m_isDefault = FALSE;
66 }
67
68 bool wxButton::Create(wxWindow *parent,
69 wxWindowID id,
70 const wxBitmap& bitmap,
71 const wxString &label,
72 const wxPoint &pos,
73 const wxSize &size,
74 long style,
75 const wxValidator& validator,
76 const wxString &name)
77 {
78 // center label by default
79 if ( !(style & wxALIGN_MASK) )
80 {
81 style |= wxALIGN_CENTRE_HORIZONTAL | wxALIGN_CENTRE_VERTICAL;
82 }
83
84 if ( !wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name) )
85 return FALSE;
86
87 SetLabel(label);
88 SetImageLabel(bitmap);
89 // SetBestSize(size); -- called by SetImageLabel()
90
91 CreateInputHandler(wxINP_HANDLER_BUTTON);
92
93 return TRUE;
94 }
95
96 wxButton::~wxButton()
97 {
98 }
99
100 // ----------------------------------------------------------------------------
101 // size management
102 // ----------------------------------------------------------------------------
103
104 /* static */
105 wxSize wxButtonBase::GetDefaultSize()
106 {
107 static wxSize s_sizeBtn;
108
109 if ( s_sizeBtn.x == 0 )
110 {
111 wxScreenDC dc;
112
113 // this corresponds more or less to wxMSW standard in Win32 theme (see
114 // wxWin32Renderer::AdjustSize())
115 s_sizeBtn.x = 8*dc.GetCharWidth();
116 s_sizeBtn.y = (11*dc.GetCharHeight())/10 + 2;
117 }
118
119 return s_sizeBtn;
120 }
121
122 wxSize wxButton::DoGetBestClientSize() const
123 {
124 wxClientDC dc(wxConstCast(this, wxButton));
125 wxCoord width, height;
126 dc.GetMultiLineTextExtent(GetLabel(), &width, &height);
127
128 if ( m_bitmap.Ok() )
129 {
130 // allocate extra space for the bitmap
131 wxCoord heightBmp = m_bitmap.GetHeight() + 2*m_marginBmpY;
132 if ( height < heightBmp )
133 height = heightBmp;
134
135 width += m_bitmap.GetWidth() + 2*m_marginBmpX;
136 }
137
138 // for compatibility with other ports, the buttons default size is never
139 // less than the standard one
140 #ifndef __WXX11__
141 if ( !(GetWindowStyle() & wxBU_EXACTFIT) )
142 {
143 wxSize szDef = GetDefaultSize();
144 if ( width < szDef.x )
145 width = szDef.x;
146 }
147 #endif
148
149 return wxSize(width, height);
150 }
151
152 // ----------------------------------------------------------------------------
153 // drawing
154 // ----------------------------------------------------------------------------
155
156 void wxButton::DoDraw(wxControlRenderer *renderer)
157 {
158 if ( !(GetWindowStyle() & wxBORDER_NONE) )
159 {
160 renderer->DrawButtonBorder();
161 }
162
163 renderer->DrawLabel(m_bitmap, m_marginBmpX, m_marginBmpY);
164 }
165
166 bool wxButton::DoDrawBackground(wxDC& dc)
167 {
168 wxRect rect;
169 wxSize size = GetSize();
170 rect.width = size.x;
171 rect.height = size.y;
172
173 if ( GetBackgroundBitmap().Ok() )
174 {
175 // get the bitmap and the flags
176 int alignment;
177 wxStretch stretch;
178 wxBitmap bmp = GetBackgroundBitmap(&alignment, &stretch);
179 wxControlRenderer::DrawBitmap(dc, bmp, rect, alignment, stretch);
180 }
181 else
182 {
183 m_renderer->DrawButtonSurface(dc, wxTHEME_BG_COLOUR(this),
184 rect, GetStateFlags());
185 }
186
187 return TRUE;
188 }
189
190 // ----------------------------------------------------------------------------
191 // input processing
192 // ----------------------------------------------------------------------------
193
194 void wxButton::Press()
195 {
196 if ( !m_isPressed )
197 {
198 m_isPressed = TRUE;
199
200 Refresh();
201 }
202 }
203
204 void wxButton::Release()
205 {
206 if ( m_isPressed )
207 {
208 m_isPressed = FALSE;
209
210 Refresh();
211 }
212 }
213
214 void wxButton::Toggle()
215 {
216 if ( m_isPressed )
217 Release();
218 else
219 Press();
220
221 if ( !m_isPressed )
222 {
223 // releasing button after it had been pressed generates a click event
224 Click();
225 }
226 }
227
228 void wxButton::Click()
229 {
230 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
231 InitCommandEvent(event);
232 Command(event);
233 }
234
235 bool wxButton::PerformAction(const wxControlAction& action,
236 long numArg,
237 const wxString& strArg)
238 {
239 if ( action == wxACTION_BUTTON_TOGGLE )
240 Toggle();
241 else if ( action == wxACTION_BUTTON_CLICK )
242 Click();
243 else if ( action == wxACTION_BUTTON_PRESS )
244 Press();
245 else if ( action == wxACTION_BUTTON_RELEASE )
246 Release();
247 else
248 return wxControl::PerformAction(action, numArg, strArg);
249
250 return TRUE;
251 }
252
253 // ----------------------------------------------------------------------------
254 // misc
255 // ----------------------------------------------------------------------------
256
257 void wxButton::SetImageLabel(const wxBitmap& bitmap)
258 {
259 m_bitmap = bitmap;
260
261 SetImageMargins(DEFAULT_BTN_MARGIN_X, DEFAULT_BTN_MARGIN_Y);
262 }
263
264 void wxButton::SetImageMargins(wxCoord x, wxCoord y)
265 {
266 m_marginBmpX = x + 2;
267 m_marginBmpY = y + 2;
268
269 SetBestSize(wxDefaultSize);
270 }
271
272 void wxButton::SetDefault()
273 {
274 m_isDefault = TRUE;
275 }
276
277 // ============================================================================
278 // wxStdButtonInputHandler
279 // ============================================================================
280
281 wxStdButtonInputHandler::wxStdButtonInputHandler(wxInputHandler *handler)
282 : wxStdInputHandler(handler)
283 {
284 m_winCapture = NULL;
285 m_winHasMouse = FALSE;
286 }
287
288 bool wxStdButtonInputHandler::HandleKey(wxInputConsumer *consumer,
289 const wxKeyEvent& event,
290 bool pressed)
291 {
292 int keycode = event.GetKeyCode();
293 if ( keycode == WXK_SPACE || keycode == WXK_RETURN )
294 {
295 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
296
297 return TRUE;
298 }
299
300 return wxStdInputHandler::HandleKey(consumer, event, pressed);
301 }
302
303 bool wxStdButtonInputHandler::HandleMouse(wxInputConsumer *consumer,
304 const wxMouseEvent& event)
305 {
306 // the button has 2 states: pressed and normal with the following
307 // transitions between them:
308 //
309 // normal -> left down -> capture mouse and go to pressed state
310 // pressed -> left up inside -> generate click -> go to normal
311 // outside ------------------>
312 //
313 // the other mouse buttons are ignored
314 if ( event.Button(1) )
315 {
316 if ( event.LeftDown() || event.LeftDClick() )
317 {
318 m_winCapture = consumer->GetInputWindow();
319 m_winCapture->CaptureMouse();
320 m_winHasMouse = TRUE;
321
322 consumer->PerformAction(wxACTION_BUTTON_PRESS);
323
324 return TRUE;
325 }
326 else if ( event.LeftUp() )
327 {
328 if ( m_winCapture )
329 {
330 m_winCapture->ReleaseMouse();
331 m_winCapture = NULL;
332 }
333
334 if ( m_winHasMouse )
335 {
336 // this will generate a click event
337 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
338
339 return TRUE;
340 }
341 //else: the mouse was released outside the window, this doesn't
342 // count as a click
343 }
344 //else: don't do anything special about the double click
345 }
346
347 return wxStdInputHandler::HandleMouse(consumer, event);
348 }
349
350 bool wxStdButtonInputHandler::HandleMouseMove(wxInputConsumer *consumer,
351 const wxMouseEvent& event)
352 {
353 // we only have to do something when the mouse leaves/enters the pressed
354 // button and don't care about the other ones
355 if ( event.GetEventObject() == m_winCapture )
356 {
357 // leaving the button should remove its pressed state
358 if ( event.Leaving() )
359 {
360 // remember that the mouse is now outside
361 m_winHasMouse = FALSE;
362
363 // we do have a pressed button, so release it
364 consumer->GetInputWindow()->SetCurrent(FALSE);
365 consumer->PerformAction(wxACTION_BUTTON_RELEASE);
366
367 return TRUE;
368 }
369 // and entering it back should make it pressed again if it had been
370 // pressed
371 else if ( event.Entering() )
372 {
373 // the mouse is (back) inside the button
374 m_winHasMouse = TRUE;
375
376 // we did have a pressed button which we released when leaving the
377 // window, press it again
378 consumer->GetInputWindow()->SetCurrent(TRUE);
379 consumer->PerformAction(wxACTION_BUTTON_PRESS);
380
381 return TRUE;
382 }
383 }
384
385 return wxStdInputHandler::HandleMouseMove(consumer, event);
386 }
387
388 bool wxStdButtonInputHandler::HandleFocus(wxInputConsumer *consumer,
389 const wxFocusEvent& event)
390 {
391 // buttons change appearance when they get/lose focus, so return TRUE to
392 // refresh
393 return TRUE;
394 }
395
396 bool wxStdButtonInputHandler::HandleActivation(wxInputConsumer *consumer,
397 bool activated)
398 {
399 // the default button changes appearance when the app is [de]activated, so
400 // return TRUE to refresh
401 return wxStaticCast(consumer->GetInputWindow(), wxButton)->IsDefault();
402 }
403
404 #endif // wxUSE_BUTTON
405