1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/carbon/renderer.cpp
3 // Purpose: implementation of wxRendererNative for Mac
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // License: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // for compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
20 #include "wx/string.h"
22 #include "wx/bitmap.h"
23 #include "wx/settings.h"
24 #include "wx/dcclient.h"
25 #include "wx/toplevel.h"
28 #include "wx/renderer.h"
29 #include "wx/graphics.h"
30 #include "wx/dcgraph.h"
31 #include "wx/osx/private.h"
34 // bring in the theme headers
35 #include <Carbon/Carbon.h>
38 // check if we're currently in a paint event
39 inline bool wxInPaintEvent(wxWindow
* win
, wxDC
& dc
)
41 return ( win
->MacGetCGContextRef() != NULL
);
46 class WXDLLEXPORT wxRendererMac
: public wxDelegateRendererNative
49 // draw the header control button (used by wxListCtrl)
50 virtual int DrawHeaderButton( wxWindow
*win
,
54 wxHeaderSortIconType sortArrow
= wxHDR_SORT_ICON_NONE
,
55 wxHeaderButtonParams
* params
= NULL
);
57 virtual int GetHeaderButtonHeight(wxWindow
*win
);
59 // draw the expanded/collapsed icon for a tree control item
60 virtual void DrawTreeItemButton( wxWindow
*win
,
65 // draw a (vertical) sash
66 virtual void DrawSplitterSash( wxWindow
*win
,
73 virtual void DrawCheckBox(wxWindow
*win
,
78 virtual wxSize
GetCheckBoxSize() const;
80 virtual void DrawComboBoxDropButton(wxWindow
*win
,
85 virtual void DrawPushButton(wxWindow
*win
,
90 virtual void DrawItemSelectionRect(wxWindow
*win
,
95 virtual void DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
= 0);
97 virtual void DrawChoice(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
=0);
99 virtual void DrawComboBox(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
=0);
101 virtual void DrawTextCtrl(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
=0);
103 virtual void DrawRadioButton(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
=0);
106 void DrawMacThemeButton(wxWindow
*win
,
114 wxBitmap m_bmpTreeExpanded
;
115 wxBitmap m_bmpTreeCollapsed
;
118 // ============================================================================
120 // ============================================================================
123 wxRendererNative
& wxRendererNative::GetDefault()
125 static wxRendererMac s_rendererMac
;
127 return s_rendererMac
;
130 int wxRendererMac::DrawHeaderButton( wxWindow
*win
,
134 wxHeaderSortIconType sortArrow
,
135 wxHeaderButtonParams
* params
)
137 const wxCoord x
= rect
.x
;
138 const wxCoord y
= rect
.y
;
139 const wxCoord w
= rect
.width
;
140 const wxCoord h
= rect
.height
;
142 dc
.SetBrush( *wxTRANSPARENT_BRUSH
);
144 HIRect headerRect
= CGRectMake( x
, y
, w
, h
);
145 if ( !wxInPaintEvent(win
, dc
) )
147 win
->Refresh( &rect
);
151 CGContextRef cgContext
;
152 wxGCDCImpl
*impl
= (wxGCDCImpl
*) dc
.GetImpl();
154 cgContext
= (CGContextRef
) impl
->GetGraphicsContext()->GetNativeContext();
157 HIThemeButtonDrawInfo drawInfo
;
160 memset( &drawInfo
, 0, sizeof(drawInfo
) );
161 drawInfo
.version
= 0;
162 drawInfo
.kind
= kThemeListHeaderButton
;
163 drawInfo
.state
= (flags
& wxCONTROL_DISABLED
) ? kThemeStateInactive
: kThemeStateActive
;
164 drawInfo
.value
= (flags
& wxCONTROL_SELECTED
) ? kThemeButtonOn
: kThemeButtonOff
;
165 drawInfo
.adornment
= kThemeAdornmentNone
;
167 // The down arrow is drawn automatically, change it to an up arrow if needed.
168 if ( sortArrow
== wxHDR_SORT_ICON_UP
)
169 drawInfo
.adornment
= kThemeAdornmentHeaderButtonSortUp
;
171 HIThemeDrawButton( &headerRect
, &drawInfo
, cgContext
, kHIThemeOrientationNormal
, &labelRect
);
173 // If we don't want any arrows we need to draw over the one already there
174 if ( (flags
& wxCONTROL_SELECTED
) && (sortArrow
== wxHDR_SORT_ICON_NONE
) )
176 // clip to the header rectangle
177 CGContextSaveGState( cgContext
);
178 CGContextClipToRect( cgContext
, headerRect
);
179 // but draw bigger than that so the arrow will get clipped off
180 headerRect
.size
.width
+= 25;
181 HIThemeDrawButton( &headerRect
, &drawInfo
, cgContext
, kHIThemeOrientationNormal
, &labelRect
);
182 CGContextRestoreGState( cgContext
);
187 // Reserve room for the arrows before writing the label, and turn off the
188 // flags we've already handled
189 wxRect
newRect(rect
);
190 if ( (flags
& wxCONTROL_SELECTED
) && (sortArrow
!= wxHDR_SORT_ICON_NONE
) )
193 sortArrow
= wxHDR_SORT_ICON_NONE
;
195 flags
&= ~wxCONTROL_SELECTED
;
197 return DrawHeaderButtonContents(win
, dc
, newRect
, flags
, sortArrow
, params
);
201 int wxRendererMac::GetHeaderButtonHeight(wxWindow
* WXUNUSED(win
))
203 SInt32 standardHeight
;
206 errStatus
= GetThemeMetric( kThemeMetricListHeaderHeight
, &standardHeight
);
207 if (errStatus
== noErr
)
209 return standardHeight
;
214 void wxRendererMac::DrawTreeItemButton( wxWindow
*win
,
219 // now the wxGCDC is using native transformations
220 const wxCoord x
= rect
.x
;
221 const wxCoord y
= rect
.y
;
222 const wxCoord w
= rect
.width
;
223 const wxCoord h
= rect
.height
;
225 dc
.SetBrush( *wxTRANSPARENT_BRUSH
);
227 HIRect headerRect
= CGRectMake( x
, y
, w
, h
);
228 if ( !wxInPaintEvent(win
, dc
) )
230 win
->Refresh( &rect
);
234 CGContextRef cgContext
;
236 wxGCDCImpl
*impl
= (wxGCDCImpl
*) dc
.GetImpl();
237 cgContext
= (CGContextRef
) impl
->GetGraphicsContext()->GetNativeContext();
239 HIThemeButtonDrawInfo drawInfo
;
242 memset( &drawInfo
, 0, sizeof(drawInfo
) );
243 drawInfo
.version
= 0;
244 drawInfo
.kind
= kThemeDisclosureButton
;
245 drawInfo
.state
= (flags
& wxCONTROL_DISABLED
) ? kThemeStateInactive
: kThemeStateActive
;
246 // Apple mailing list posts say to use the arrow adornment constants, but those don't work.
247 // We need to set the value using the 'old' DrawThemeButton constants instead.
248 drawInfo
.value
= (flags
& wxCONTROL_EXPANDED
) ? kThemeDisclosureDown
: kThemeDisclosureRight
;
249 drawInfo
.adornment
= kThemeAdornmentNone
;
251 HIThemeDrawButton( &headerRect
, &drawInfo
, cgContext
, kHIThemeOrientationNormal
, &labelRect
);
255 void wxRendererMac::DrawSplitterSash( wxWindow
*win
,
259 wxOrientation orient
,
260 int WXUNUSED(flags
) )
262 bool hasMetal
= win
->MacGetTopLevelWindow()->GetExtraStyle() & wxFRAME_EX_METAL
;
264 GetThemeMetric( kThemeMetricSmallPaneSplitterHeight
, &height
);
266 if (orient
== wxVERTICAL
)
267 splitterRect
= CGRectMake( position
, 0, height
, size
.y
);
269 splitterRect
= CGRectMake( 0, position
, size
.x
, height
);
271 // under compositing we should only draw when called by the OS, otherwise just issue a redraw command
272 // strange redraw errors occur if we don't do this
274 if ( !wxInPaintEvent(win
, dc
) )
276 wxRect
rect( (int) splitterRect
.origin
.x
, (int) splitterRect
.origin
.y
, (int) splitterRect
.size
.width
,
277 (int) splitterRect
.size
.height
);
278 win
->Refresh( &rect
);
282 CGContextRef cgContext
;
283 wxGCDCImpl
*impl
= (wxGCDCImpl
*) dc
.GetImpl();
284 cgContext
= (CGContextRef
) impl
->GetGraphicsContext()->GetNativeContext();
286 HIThemeSplitterDrawInfo drawInfo
;
287 drawInfo
.version
= 0;
288 drawInfo
.state
= kThemeStateActive
;
289 drawInfo
.adornment
= hasMetal
? kHIThemeSplitterAdornmentMetal
: kHIThemeSplitterAdornmentNone
;
290 HIThemeDrawPaneSplitter( &splitterRect
, &drawInfo
, cgContext
, kHIThemeOrientationNormal
);
295 wxRendererMac::DrawItemSelectionRect(wxWindow
* WXUNUSED(win
),
300 if ( !(flags
& wxCONTROL_SELECTED
) )
303 wxColour
col( wxMacCreateCGColorFromHITheme( (flags
& wxCONTROL_FOCUSED
) ?
304 kThemeBrushAlternatePrimaryHighlightColor
305 : kThemeBrushSecondaryHighlightColor
) );
306 wxBrush
selBrush( col
);
308 dc
.SetPen( *wxTRANSPARENT_PEN
);
309 dc
.SetBrush( selBrush
);
310 dc
.DrawRectangle( rect
);
315 wxRendererMac::DrawMacThemeButton(wxWindow
*win
,
322 // now the wxGCDC is using native transformations
323 const wxCoord x
= rect
.x
;
324 const wxCoord y
= rect
.y
;
325 const wxCoord w
= rect
.width
;
326 const wxCoord h
= rect
.height
;
328 dc
.SetBrush( *wxTRANSPARENT_BRUSH
);
330 HIRect headerRect
= CGRectMake( x
, y
, w
, h
);
331 if ( !wxInPaintEvent(win
, dc
) )
333 win
->Refresh( &rect
);
337 wxGCDCImpl
*impl
= (wxGCDCImpl
*) dc
.GetImpl();
338 CGContextRef cgContext
;
339 cgContext
= (CGContextRef
) impl
->GetGraphicsContext()->GetNativeContext();
341 HIThemeButtonDrawInfo drawInfo
;
344 memset( &drawInfo
, 0, sizeof(drawInfo
) );
345 drawInfo
.version
= 0;
346 drawInfo
.kind
= kind
;
347 drawInfo
.state
= (flags
& wxCONTROL_DISABLED
) ? kThemeStateInactive
: kThemeStateActive
;
348 drawInfo
.value
= (flags
& wxCONTROL_SELECTED
) ? kThemeButtonOn
: kThemeButtonOff
;
349 if (flags
& wxCONTROL_UNDETERMINED
)
350 drawInfo
.value
= kThemeButtonMixed
;
351 drawInfo
.adornment
= adornment
;
352 if (flags
& wxCONTROL_FOCUSED
)
353 drawInfo
.adornment
|= kThemeAdornmentFocus
;
355 HIThemeDrawButton( &headerRect
, &drawInfo
, cgContext
, kHIThemeOrientationNormal
, &labelRect
);
360 wxRendererMac::DrawCheckBox(wxWindow
*win
,
365 if (flags
& wxCONTROL_CHECKED
)
366 flags
|= wxCONTROL_SELECTED
;
370 if (win
->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
||
371 (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
))
372 kind
= kThemeCheckBoxSmall
;
373 else if (win
->GetWindowVariant() == wxWINDOW_VARIANT_MINI
||
374 (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI
))
375 kind
= kThemeCheckBoxMini
;
377 kind
= kThemeCheckBox
;
380 DrawMacThemeButton(win
, dc
, rect
, flags
,
381 kind
, kThemeAdornmentNone
);
384 wxSize
wxRendererMac::GetCheckBoxSize() const
387 SInt32 width
, height
;
390 errStatus
= GetThemeMetric(kThemeMetricCheckBoxWidth
, &width
);
391 if (errStatus
== noErr
)
393 size
.SetWidth(width
);
396 errStatus
= GetThemeMetric(kThemeMetricCheckBoxHeight
, &height
);
397 if (errStatus
== noErr
)
399 size
.SetHeight(height
);
406 wxRendererMac::DrawComboBoxDropButton(wxWindow
*win
,
412 if (win
->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
|| (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
))
413 kind
= kThemeArrowButtonSmall
;
414 else if (win
->GetWindowVariant() == wxWINDOW_VARIANT_MINI
|| (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI
))
415 kind
= kThemeArrowButtonMini
;
417 kind
= kThemeArrowButton
;
419 DrawMacThemeButton(win
, dc
, rect
, flags
,
420 kind
, kThemeAdornmentArrowDownArrow
);
424 wxRendererMac::DrawPushButton(wxWindow
*win
,
430 if (win
->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
|| (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
))
431 kind
= kThemeBevelButtonSmall
;
432 // There is no kThemeBevelButtonMini, but in this case, use Small
433 else if (win
->GetWindowVariant() == wxWINDOW_VARIANT_MINI
|| (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI
))
434 kind
= kThemeBevelButtonSmall
;
436 kind
= kThemeBevelButton
;
438 DrawMacThemeButton(win
, dc
, rect
, flags
,
439 kind
, kThemeAdornmentNone
);
443 wxRendererMac::DrawFocusRect(wxWindow
* win
, wxDC
& dc
, const wxRect
& rect
, int flags
)
447 wxDelegateRendererNative::DrawFocusRect(win
, dc
, rect
, flags
);
451 CGRect cgrect
= CGRectMake( rect
.x
, rect
.y
, rect
.width
, rect
.height
) ;
453 HIThemeFrameDrawInfo info
;
455 memset( &info
, 0 , sizeof(info
) ) ;
459 info
.state
= kThemeStateActive
;
460 info
.isFocused
= true ;
462 CGContextRef cgContext
= (CGContextRef
) win
->MacGetCGContextRef() ;
463 wxASSERT( cgContext
) ;
465 HIThemeDrawFocusRect( &cgrect
, true , cgContext
, kHIThemeOrientationNormal
) ;
468 void wxRendererMac::DrawChoice(wxWindow
* win
, wxDC
& dc
,
469 const wxRect
& rect
, int flags
)
473 if (win
->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
||
474 (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
))
475 kind
= kThemePopupButtonSmall
;
476 else if (win
->GetWindowVariant() == wxWINDOW_VARIANT_MINI
||
477 (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI
))
478 kind
= kThemePopupButtonMini
;
480 kind
= kThemePopupButton
;
482 DrawMacThemeButton(win
, dc
, rect
, flags
, kind
, kThemeAdornmentNone
);
486 void wxRendererMac::DrawComboBox(wxWindow
* win
, wxDC
& dc
,
487 const wxRect
& rect
, int flags
)
491 if (win
->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
||
492 (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
))
493 kind
= kThemeComboBoxSmall
;
494 else if (win
->GetWindowVariant() == wxWINDOW_VARIANT_MINI
||
495 (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI
))
496 kind
= kThemeComboBoxMini
;
498 kind
= kThemeComboBox
;
500 DrawMacThemeButton(win
, dc
, rect
, flags
, kind
, kThemeAdornmentNone
);
503 void wxRendererMac::DrawRadioButton(wxWindow
* win
, wxDC
& dc
,
504 const wxRect
& rect
, int flags
)
508 if (win
->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
||
509 (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL
))
510 kind
= kThemeRadioButtonSmall
;
511 else if (win
->GetWindowVariant() == wxWINDOW_VARIANT_MINI
||
512 (win
->GetParent() && win
->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI
))
513 kind
= kThemeRadioButtonMini
;
515 kind
= kThemeRadioButton
;
517 if (flags
& wxCONTROL_CHECKED
)
518 flags
|= wxCONTROL_SELECTED
;
520 DrawMacThemeButton(win
, dc
, rect
, flags
,
521 kind
, kThemeAdornmentNone
);
524 void wxRendererMac::DrawTextCtrl(wxWindow
* win
, wxDC
& dc
,
525 const wxRect
& rect
, int flags
)
527 const wxCoord x
= rect
.x
;
528 const wxCoord y
= rect
.y
;
529 const wxCoord w
= rect
.width
;
530 const wxCoord h
= rect
.height
;
532 dc
.SetBrush( *wxWHITE_BRUSH
);
533 dc
.SetPen( *wxTRANSPARENT_PEN
);
534 dc
.DrawRectangle(rect
);
536 dc
.SetBrush( *wxTRANSPARENT_BRUSH
);
538 HIRect hiRect
= CGRectMake( x
, y
, w
, h
);
539 if ( !wxInPaintEvent(win
, dc
) )
541 win
->Refresh( &rect
);
545 CGContextRef cgContext
;
547 cgContext
= (CGContextRef
) static_cast<wxGCDCImpl
*>(dc
.GetImpl())->GetGraphicsContext()->GetNativeContext();
550 HIThemeFrameDrawInfo drawInfo
;
552 memset( &drawInfo
, 0, sizeof(drawInfo
) );
553 drawInfo
.version
= 0;
554 drawInfo
.kind
= kHIThemeFrameTextFieldSquare
;
555 drawInfo
.state
= (flags
& wxCONTROL_DISABLED
) ? kThemeStateInactive
: kThemeStateActive
;
556 if (flags
& wxCONTROL_FOCUSED
)
557 drawInfo
.isFocused
= true;
559 HIThemeDrawFrame( &hiRect
, &drawInfo
, cgContext
, kHIThemeOrientationNormal
);