]> git.saurik.com Git - wxWidgets.git/blame - src/osx/carbon/renderer.cpp
avoid overdrawing, fixes #10865
[wxWidgets.git] / src / osx / carbon / renderer.cpp
CommitLineData
489468fe 1///////////////////////////////////////////////////////////////////////////////
524c47aa 2// Name: src/osx/carbon/renderer.cpp
489468fe
SC
3// Purpose: implementation of wxRendererNative for Mac
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 20.07.2003
7// RCS-ID: $Id$
8// Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9// License: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12// for compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
16 #pragma hdrstop
17#endif
18
19#ifndef WX_PRECOMP
20 #include "wx/string.h"
21 #include "wx/dc.h"
22 #include "wx/bitmap.h"
23 #include "wx/settings.h"
24 #include "wx/dcclient.h"
25 #include "wx/toplevel.h"
26#endif
27
28#include "wx/renderer.h"
29#include "wx/graphics.h"
524c47aa 30#include "wx/dcgraph.h"
b2680ced 31#include "wx/osx/private.h"
489468fe 32
524c47aa
SC
33#if wxOSX_USE_COCOA
34// bring in the theme headers
35#include <Carbon/Carbon.h>
36#endif
489468fe 37
e4131985
KO
38// check if we're currently in a paint event
39inline bool wxInPaintEvent(wxWindow* win, wxDC& dc)
40{
de0d2095 41 wxUnusedVar(dc);
e4131985
KO
42 return ( win->MacGetCGContextRef() != NULL );
43}
44
45
46
489468fe
SC
47class WXDLLEXPORT wxRendererMac : public wxDelegateRendererNative
48{
49public:
50 // draw the header control button (used by wxListCtrl)
51 virtual int DrawHeaderButton( wxWindow *win,
52 wxDC& dc,
53 const wxRect& rect,
54 int flags = 0,
55 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
56 wxHeaderButtonParams* params = NULL );
57
58 virtual int GetHeaderButtonHeight(wxWindow *win);
59
60 // draw the expanded/collapsed icon for a tree control item
61 virtual void DrawTreeItemButton( wxWindow *win,
62 wxDC& dc,
63 const wxRect& rect,
64 int flags = 0 );
65
66 // draw a (vertical) sash
67 virtual void DrawSplitterSash( wxWindow *win,
68 wxDC& dc,
69 const wxSize& size,
70 wxCoord position,
71 wxOrientation orient,
72 int flags = 0 );
73
74 virtual void DrawCheckBox(wxWindow *win,
75 wxDC& dc,
76 const wxRect& rect,
77 int flags = 0);
78
191e43fd 79 virtual wxSize GetCheckBoxSize(wxWindow* win);
e8759560 80
489468fe
SC
81 virtual void DrawComboBoxDropButton(wxWindow *win,
82 wxDC& dc,
83 const wxRect& rect,
84 int flags = 0);
85
86 virtual void DrawPushButton(wxWindow *win,
87 wxDC& dc,
88 const wxRect& rect,
89 int flags = 0);
90
91 virtual void DrawItemSelectionRect(wxWindow *win,
92 wxDC& dc,
93 const wxRect& rect,
94 int flags = 0);
95
96 virtual void DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags = 0);
97
e4131985
KO
98 virtual void DrawChoice(wxWindow* win, wxDC& dc, const wxRect& rect, int flags=0);
99
100 virtual void DrawComboBox(wxWindow* win, wxDC& dc, const wxRect& rect, int flags=0);
101
102 virtual void DrawTextCtrl(wxWindow* win, wxDC& dc, const wxRect& rect, int flags=0);
103
a2ee1945 104 virtual void DrawRadioButton(wxWindow* win, wxDC& dc, const wxRect& rect, int flags=0);
e4131985 105
489468fe
SC
106private:
107 void DrawMacThemeButton(wxWindow *win,
108 wxDC& dc,
109 const wxRect& rect,
110 int flags,
111 int kind,
112 int adornment);
113
114 // the tree buttons
115 wxBitmap m_bmpTreeExpanded;
116 wxBitmap m_bmpTreeCollapsed;
117};
118
119// ============================================================================
120// implementation
121// ============================================================================
122
123// static
124wxRendererNative& wxRendererNative::GetDefault()
125{
126 static wxRendererMac s_rendererMac;
127
128 return s_rendererMac;
129}
130
131int wxRendererMac::DrawHeaderButton( wxWindow *win,
132 wxDC& dc,
133 const wxRect& rect,
134 int flags,
135 wxHeaderSortIconType sortArrow,
136 wxHeaderButtonParams* params )
137{
138 const wxCoord x = rect.x;
139 const wxCoord y = rect.y;
140 const wxCoord w = rect.width;
141 const wxCoord h = rect.height;
142
143 dc.SetBrush( *wxTRANSPARENT_BRUSH );
144
145 HIRect headerRect = CGRectMake( x, y, w, h );
e4131985 146 if ( !wxInPaintEvent(win, dc) )
489468fe
SC
147 {
148 win->Refresh( &rect );
149 }
150 else
151 {
152 CGContextRef cgContext;
153 wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl();
154
155 cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext();
156
157 {
158 HIThemeButtonDrawInfo drawInfo;
159 HIRect labelRect;
160
161 memset( &drawInfo, 0, sizeof(drawInfo) );
162 drawInfo.version = 0;
163 drawInfo.kind = kThemeListHeaderButton;
164 drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive;
165 drawInfo.value = (flags & wxCONTROL_SELECTED) ? kThemeButtonOn : kThemeButtonOff;
166 drawInfo.adornment = kThemeAdornmentNone;
167
168 // The down arrow is drawn automatically, change it to an up arrow if needed.
169 if ( sortArrow == wxHDR_SORT_ICON_UP )
170 drawInfo.adornment = kThemeAdornmentHeaderButtonSortUp;
171
172 HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect );
173
174 // If we don't want any arrows we need to draw over the one already there
175 if ( (flags & wxCONTROL_SELECTED) && (sortArrow == wxHDR_SORT_ICON_NONE) )
176 {
177 // clip to the header rectangle
178 CGContextSaveGState( cgContext );
179 CGContextClipToRect( cgContext, headerRect );
180 // but draw bigger than that so the arrow will get clipped off
181 headerRect.size.width += 25;
182 HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect );
183 CGContextRestoreGState( cgContext );
184 }
185 }
186 }
187
188 // Reserve room for the arrows before writing the label, and turn off the
189 // flags we've already handled
190 wxRect newRect(rect);
191 if ( (flags & wxCONTROL_SELECTED) && (sortArrow != wxHDR_SORT_ICON_NONE) )
192 {
193 newRect.width -= 12;
194 sortArrow = wxHDR_SORT_ICON_NONE;
195 }
196 flags &= ~wxCONTROL_SELECTED;
197
198 return DrawHeaderButtonContents(win, dc, newRect, flags, sortArrow, params);
199}
200
201
202int wxRendererMac::GetHeaderButtonHeight(wxWindow* WXUNUSED(win))
203{
204 SInt32 standardHeight;
205 OSStatus errStatus;
206
207 errStatus = GetThemeMetric( kThemeMetricListHeaderHeight, &standardHeight );
208 if (errStatus == noErr)
209 {
210 return standardHeight;
211 }
212 return -1;
213}
214
215void wxRendererMac::DrawTreeItemButton( wxWindow *win,
216 wxDC& dc,
217 const wxRect& rect,
218 int flags )
219{
220 // now the wxGCDC is using native transformations
221 const wxCoord x = rect.x;
222 const wxCoord y = rect.y;
223 const wxCoord w = rect.width;
224 const wxCoord h = rect.height;
225
226 dc.SetBrush( *wxTRANSPARENT_BRUSH );
227
228 HIRect headerRect = CGRectMake( x, y, w, h );
e4131985 229 if ( !wxInPaintEvent(win, dc) )
489468fe
SC
230 {
231 win->Refresh( &rect );
232 }
233 else
234 {
235 CGContextRef cgContext;
236
237 wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl();
238 cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext();
239
240 HIThemeButtonDrawInfo drawInfo;
241 HIRect labelRect;
242
243 memset( &drawInfo, 0, sizeof(drawInfo) );
244 drawInfo.version = 0;
245 drawInfo.kind = kThemeDisclosureButton;
246 drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive;
247 // Apple mailing list posts say to use the arrow adornment constants, but those don't work.
248 // We need to set the value using the 'old' DrawThemeButton constants instead.
249 drawInfo.value = (flags & wxCONTROL_EXPANDED) ? kThemeDisclosureDown : kThemeDisclosureRight;
250 drawInfo.adornment = kThemeAdornmentNone;
251
252 HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect );
253 }
254}
255
256void wxRendererMac::DrawSplitterSash( wxWindow *win,
257 wxDC& dc,
258 const wxSize& size,
259 wxCoord position,
260 wxOrientation orient,
261 int WXUNUSED(flags) )
262{
b2680ced 263 bool hasMetal = win->MacGetTopLevelWindow()->GetExtraStyle() & wxFRAME_EX_METAL;
489468fe
SC
264 SInt32 height;
265 GetThemeMetric( kThemeMetricSmallPaneSplitterHeight, &height );
266 HIRect splitterRect;
267 if (orient == wxVERTICAL)
268 splitterRect = CGRectMake( position, 0, height, size.y );
269 else
270 splitterRect = CGRectMake( 0, position, size.x, height );
271
272 // under compositing we should only draw when called by the OS, otherwise just issue a redraw command
273 // strange redraw errors occur if we don't do this
274
e4131985 275 if ( !wxInPaintEvent(win, dc) )
489468fe
SC
276 {
277 wxRect rect( (int) splitterRect.origin.x, (int) splitterRect.origin.y, (int) splitterRect.size.width,
278 (int) splitterRect.size.height );
279 win->Refresh( &rect );
280 }
281 else
282 {
283 CGContextRef cgContext;
284 wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl();
285 cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext();
286
287 HIThemeSplitterDrawInfo drawInfo;
288 drawInfo.version = 0;
289 drawInfo.state = kThemeStateActive;
290 drawInfo.adornment = hasMetal ? kHIThemeSplitterAdornmentMetal : kHIThemeSplitterAdornmentNone;
291 HIThemeDrawPaneSplitter( &splitterRect, &drawInfo, cgContext, kHIThemeOrientationNormal );
292 }
293}
294
295void
296wxRendererMac::DrawItemSelectionRect(wxWindow * WXUNUSED(win),
297 wxDC& dc,
298 const wxRect& rect,
299 int flags)
300{
301 if ( !(flags & wxCONTROL_SELECTED) )
302 return;
303
304 wxColour col( wxMacCreateCGColorFromHITheme( (flags & wxCONTROL_FOCUSED) ?
305 kThemeBrushAlternatePrimaryHighlightColor
306 : kThemeBrushSecondaryHighlightColor ) );
307 wxBrush selBrush( col );
308
309 dc.SetPen( *wxTRANSPARENT_PEN );
310 dc.SetBrush( selBrush );
311 dc.DrawRectangle( rect );
312}
313
314
315void
316wxRendererMac::DrawMacThemeButton(wxWindow *win,
317 wxDC& dc,
318 const wxRect& rect,
319 int flags,
320 int kind,
321 int adornment)
322{
323 // now the wxGCDC is using native transformations
324 const wxCoord x = rect.x;
325 const wxCoord y = rect.y;
326 const wxCoord w = rect.width;
327 const wxCoord h = rect.height;
328
329 dc.SetBrush( *wxTRANSPARENT_BRUSH );
330
331 HIRect headerRect = CGRectMake( x, y, w, h );
e4131985 332 if ( !wxInPaintEvent(win, dc) )
489468fe
SC
333 {
334 win->Refresh( &rect );
335 }
336 else
337 {
338 wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl();
339 CGContextRef cgContext;
340 cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext();
341
342 HIThemeButtonDrawInfo drawInfo;
343 HIRect labelRect;
344
345 memset( &drawInfo, 0, sizeof(drawInfo) );
346 drawInfo.version = 0;
347 drawInfo.kind = kind;
348 drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive;
349 drawInfo.value = (flags & wxCONTROL_SELECTED) ? kThemeButtonOn : kThemeButtonOff;
350 if (flags & wxCONTROL_UNDETERMINED)
351 drawInfo.value = kThemeButtonMixed;
352 drawInfo.adornment = adornment;
e4131985
KO
353 if (flags & wxCONTROL_FOCUSED)
354 drawInfo.adornment |= kThemeAdornmentFocus;
99c4be68 355
489468fe
SC
356 HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect );
357 }
358}
359
360void
361wxRendererMac::DrawCheckBox(wxWindow *win,
362 wxDC& dc,
363 const wxRect& rect,
364 int flags)
365{
366 if (flags & wxCONTROL_CHECKED)
367 flags |= wxCONTROL_SELECTED;
368
e4131985 369 int kind;
99c4be68 370
e4131985
KO
371 if (win->GetWindowVariant() == wxWINDOW_VARIANT_SMALL ||
372 (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL))
373 kind = kThemeCheckBoxSmall;
374 else if (win->GetWindowVariant() == wxWINDOW_VARIANT_MINI ||
375 (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI))
376 kind = kThemeCheckBoxMini;
377 else
378 kind = kThemeCheckBox;
379
380
489468fe 381 DrawMacThemeButton(win, dc, rect, flags,
e4131985 382 kind, kThemeAdornmentNone);
489468fe
SC
383}
384
191e43fd 385wxSize wxRendererMac::GetCheckBoxSize(wxWindow* WXUNUSED(win))
e8759560
VZ
386{
387 wxSize size;
388 SInt32 width, height;
389 OSStatus errStatus;
390
391 errStatus = GetThemeMetric(kThemeMetricCheckBoxWidth, &width);
392 if (errStatus == noErr)
393 {
394 size.SetWidth(width);
395 }
396
397 errStatus = GetThemeMetric(kThemeMetricCheckBoxHeight, &height);
398 if (errStatus == noErr)
399 {
400 size.SetHeight(height);
401 }
402
403 return size;
404}
405
489468fe
SC
406void
407wxRendererMac::DrawComboBoxDropButton(wxWindow *win,
408 wxDC& dc,
409 const wxRect& rect,
410 int flags)
411{
412 int kind;
413 if (win->GetWindowVariant() == wxWINDOW_VARIANT_SMALL || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL))
414 kind = kThemeArrowButtonSmall;
415 else if (win->GetWindowVariant() == wxWINDOW_VARIANT_MINI || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI))
416 kind = kThemeArrowButtonMini;
417 else
418 kind = kThemeArrowButton;
419
420 DrawMacThemeButton(win, dc, rect, flags,
421 kind, kThemeAdornmentArrowDownArrow);
422}
423
424void
425wxRendererMac::DrawPushButton(wxWindow *win,
426 wxDC& dc,
427 const wxRect& rect,
428 int flags)
429{
430 int kind;
431 if (win->GetWindowVariant() == wxWINDOW_VARIANT_SMALL || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL))
432 kind = kThemeBevelButtonSmall;
433 // There is no kThemeBevelButtonMini, but in this case, use Small
434 else if (win->GetWindowVariant() == wxWINDOW_VARIANT_MINI || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI))
435 kind = kThemeBevelButtonSmall;
436 else
437 kind = kThemeBevelButton;
438
439 DrawMacThemeButton(win, dc, rect, flags,
440 kind, kThemeAdornmentNone);
441}
442
443void
444wxRendererMac::DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
445{
446 if (!win)
447 {
448 wxDelegateRendererNative::DrawFocusRect(win, dc, rect, flags);
449 return;
450 }
451
452 CGRect cgrect = CGRectMake( rect.x , rect.y , rect.width, rect.height ) ;
453
454 HIThemeFrameDrawInfo info ;
e4131985 455
489468fe
SC
456 memset( &info, 0 , sizeof(info) ) ;
457
458 info.version = 0 ;
459 info.kind = 0 ;
460 info.state = kThemeStateActive;
461 info.isFocused = true ;
462
463 CGContextRef cgContext = (CGContextRef) win->MacGetCGContextRef() ;
464 wxASSERT( cgContext ) ;
465
466 HIThemeDrawFocusRect( &cgrect , true , cgContext , kHIThemeOrientationNormal ) ;
467}
e4131985
KO
468
469void wxRendererMac::DrawChoice(wxWindow* win, wxDC& dc,
470 const wxRect& rect, int flags)
471{
472 int kind;
99c4be68 473
e4131985
KO
474 if (win->GetWindowVariant() == wxWINDOW_VARIANT_SMALL ||
475 (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL))
476 kind = kThemePopupButtonSmall;
477 else if (win->GetWindowVariant() == wxWINDOW_VARIANT_MINI ||
478 (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI))
479 kind = kThemePopupButtonMini;
480 else
481 kind = kThemePopupButton;
482
483 DrawMacThemeButton(win, dc, rect, flags, kind, kThemeAdornmentNone);
484}
485
486
487void wxRendererMac::DrawComboBox(wxWindow* win, wxDC& dc,
488 const wxRect& rect, int flags)
489{
490 int kind;
99c4be68 491
e4131985
KO
492 if (win->GetWindowVariant() == wxWINDOW_VARIANT_SMALL ||
493 (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL))
494 kind = kThemeComboBoxSmall;
495 else if (win->GetWindowVariant() == wxWINDOW_VARIANT_MINI ||
496 (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI))
497 kind = kThemeComboBoxMini;
498 else
499 kind = kThemeComboBox;
500
501 DrawMacThemeButton(win, dc, rect, flags, kind, kThemeAdornmentNone);
502}
503
a2ee1945 504void wxRendererMac::DrawRadioButton(wxWindow* win, wxDC& dc,
e4131985
KO
505 const wxRect& rect, int flags)
506{
507 int kind;
99c4be68 508
e4131985
KO
509 if (win->GetWindowVariant() == wxWINDOW_VARIANT_SMALL ||
510 (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL))
511 kind = kThemeRadioButtonSmall;
512 else if (win->GetWindowVariant() == wxWINDOW_VARIANT_MINI ||
513 (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI))
514 kind = kThemeRadioButtonMini;
515 else
516 kind = kThemeRadioButton;
517
518 if (flags & wxCONTROL_CHECKED)
519 flags |= wxCONTROL_SELECTED;
520
521 DrawMacThemeButton(win, dc, rect, flags,
522 kind, kThemeAdornmentNone);
523}
524
525void wxRendererMac::DrawTextCtrl(wxWindow* win, wxDC& dc,
526 const wxRect& rect, int flags)
527{
528 const wxCoord x = rect.x;
529 const wxCoord y = rect.y;
530 const wxCoord w = rect.width;
531 const wxCoord h = rect.height;
532
533 dc.SetBrush( *wxWHITE_BRUSH );
534 dc.SetPen( *wxTRANSPARENT_PEN );
535 dc.DrawRectangle(rect);
99c4be68 536
e4131985
KO
537 dc.SetBrush( *wxTRANSPARENT_BRUSH );
538
539 HIRect hiRect = CGRectMake( x, y, w, h );
540 if ( !wxInPaintEvent(win, dc) )
541 {
953e84dd 542 win->Refresh( &rect );
e4131985
KO
543 }
544 else
545 {
546 CGContextRef cgContext;
547
548 cgContext = (CGContextRef) static_cast<wxGCDCImpl*>(dc.GetImpl())->GetGraphicsContext()->GetNativeContext();
549
550 {
551 HIThemeFrameDrawInfo drawInfo;
552
553 memset( &drawInfo, 0, sizeof(drawInfo) );
554 drawInfo.version = 0;
555 drawInfo.kind = kHIThemeFrameTextFieldSquare;
556 drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive;
557 if (flags & wxCONTROL_FOCUSED)
558 drawInfo.isFocused = true;
559
560 HIThemeDrawFrame( &hiRect, &drawInfo, cgContext, kHIThemeOrientationNormal);
561 }
562 }
563}
564