add wxRenderer::GetCheckBoxSize(); refactor wxGTK code to avoid duplication (#9642)
[wxWidgets.git] / src / osx / carbon / renderer.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/carbon/renderer.cpp
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"
30 #include "wx/osx/private.h"
31
32
33 class WXDLLEXPORT wxRendererMac : public wxDelegateRendererNative
34 {
35 public:
36 // draw the header control button (used by wxListCtrl)
37 virtual int DrawHeaderButton( wxWindow *win,
38 wxDC& dc,
39 const wxRect& rect,
40 int flags = 0,
41 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
42 wxHeaderButtonParams* params = NULL );
43
44 virtual int GetHeaderButtonHeight(wxWindow *win);
45
46 // draw the expanded/collapsed icon for a tree control item
47 virtual void DrawTreeItemButton( wxWindow *win,
48 wxDC& dc,
49 const wxRect& rect,
50 int flags = 0 );
51
52 // draw a (vertical) sash
53 virtual void DrawSplitterSash( wxWindow *win,
54 wxDC& dc,
55 const wxSize& size,
56 wxCoord position,
57 wxOrientation orient,
58 int flags = 0 );
59
60 virtual void DrawCheckBox(wxWindow *win,
61 wxDC& dc,
62 const wxRect& rect,
63 int flags = 0);
64
65 virtual wxSize GetCheckBoxSize(wxWindow* win);
66
67 virtual void DrawComboBoxDropButton(wxWindow *win,
68 wxDC& dc,
69 const wxRect& rect,
70 int flags = 0);
71
72 virtual void DrawPushButton(wxWindow *win,
73 wxDC& dc,
74 const wxRect& rect,
75 int flags = 0);
76
77 virtual void DrawItemSelectionRect(wxWindow *win,
78 wxDC& dc,
79 const wxRect& rect,
80 int flags = 0);
81
82 virtual void DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags = 0);
83
84 private:
85 void DrawMacThemeButton(wxWindow *win,
86 wxDC& dc,
87 const wxRect& rect,
88 int flags,
89 int kind,
90 int adornment);
91
92 // the tree buttons
93 wxBitmap m_bmpTreeExpanded;
94 wxBitmap m_bmpTreeCollapsed;
95 };
96
97 // ============================================================================
98 // implementation
99 // ============================================================================
100
101 // static
102 wxRendererNative& wxRendererNative::GetDefault()
103 {
104 static wxRendererMac s_rendererMac;
105
106 return s_rendererMac;
107 }
108
109 int wxRendererMac::DrawHeaderButton( wxWindow *win,
110 wxDC& dc,
111 const wxRect& rect,
112 int flags,
113 wxHeaderSortIconType sortArrow,
114 wxHeaderButtonParams* params )
115 {
116 const wxCoord x = rect.x;
117 const wxCoord y = rect.y;
118 const wxCoord w = rect.width;
119 const wxCoord h = rect.height;
120
121 dc.SetBrush( *wxTRANSPARENT_BRUSH );
122
123 HIRect headerRect = CGRectMake( x, y, w, h );
124 if ( !dc.IsKindOf( CLASSINFO( wxPaintDC ) ) )
125 {
126 win->Refresh( &rect );
127 }
128 else
129 {
130 CGContextRef cgContext;
131 wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl();
132
133 cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext();
134
135 {
136 HIThemeButtonDrawInfo drawInfo;
137 HIRect labelRect;
138
139 memset( &drawInfo, 0, sizeof(drawInfo) );
140 drawInfo.version = 0;
141 drawInfo.kind = kThemeListHeaderButton;
142 drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive;
143 drawInfo.value = (flags & wxCONTROL_SELECTED) ? kThemeButtonOn : kThemeButtonOff;
144 drawInfo.adornment = kThemeAdornmentNone;
145
146 // The down arrow is drawn automatically, change it to an up arrow if needed.
147 if ( sortArrow == wxHDR_SORT_ICON_UP )
148 drawInfo.adornment = kThemeAdornmentHeaderButtonSortUp;
149
150 HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect );
151
152 // If we don't want any arrows we need to draw over the one already there
153 if ( (flags & wxCONTROL_SELECTED) && (sortArrow == wxHDR_SORT_ICON_NONE) )
154 {
155 // clip to the header rectangle
156 CGContextSaveGState( cgContext );
157 CGContextClipToRect( cgContext, headerRect );
158 // but draw bigger than that so the arrow will get clipped off
159 headerRect.size.width += 25;
160 HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect );
161 CGContextRestoreGState( cgContext );
162 }
163 }
164 }
165
166 // Reserve room for the arrows before writing the label, and turn off the
167 // flags we've already handled
168 wxRect newRect(rect);
169 if ( (flags & wxCONTROL_SELECTED) && (sortArrow != wxHDR_SORT_ICON_NONE) )
170 {
171 newRect.width -= 12;
172 sortArrow = wxHDR_SORT_ICON_NONE;
173 }
174 flags &= ~wxCONTROL_SELECTED;
175
176 return DrawHeaderButtonContents(win, dc, newRect, flags, sortArrow, params);
177 }
178
179
180 int wxRendererMac::GetHeaderButtonHeight(wxWindow* WXUNUSED(win))
181 {
182 SInt32 standardHeight;
183 OSStatus errStatus;
184
185 errStatus = GetThemeMetric( kThemeMetricListHeaderHeight, &standardHeight );
186 if (errStatus == noErr)
187 {
188 return standardHeight;
189 }
190 return -1;
191 }
192
193 void wxRendererMac::DrawTreeItemButton( wxWindow *win,
194 wxDC& dc,
195 const wxRect& rect,
196 int flags )
197 {
198 // now the wxGCDC is using native transformations
199 const wxCoord x = rect.x;
200 const wxCoord y = rect.y;
201 const wxCoord w = rect.width;
202 const wxCoord h = rect.height;
203
204 dc.SetBrush( *wxTRANSPARENT_BRUSH );
205
206 HIRect headerRect = CGRectMake( x, y, w, h );
207 if ( !dc.IsKindOf( CLASSINFO( wxPaintDC ) ) )
208 {
209 win->Refresh( &rect );
210 }
211 else
212 {
213 CGContextRef cgContext;
214
215 wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl();
216 cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext();
217
218 HIThemeButtonDrawInfo drawInfo;
219 HIRect labelRect;
220
221 memset( &drawInfo, 0, sizeof(drawInfo) );
222 drawInfo.version = 0;
223 drawInfo.kind = kThemeDisclosureButton;
224 drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive;
225 // Apple mailing list posts say to use the arrow adornment constants, but those don't work.
226 // We need to set the value using the 'old' DrawThemeButton constants instead.
227 drawInfo.value = (flags & wxCONTROL_EXPANDED) ? kThemeDisclosureDown : kThemeDisclosureRight;
228 drawInfo.adornment = kThemeAdornmentNone;
229
230 HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect );
231 }
232 }
233
234 void wxRendererMac::DrawSplitterSash( wxWindow *win,
235 wxDC& dc,
236 const wxSize& size,
237 wxCoord position,
238 wxOrientation orient,
239 int WXUNUSED(flags) )
240 {
241 bool hasMetal = win->MacGetTopLevelWindow()->GetExtraStyle() & wxFRAME_EX_METAL;
242 SInt32 height;
243 GetThemeMetric( kThemeMetricSmallPaneSplitterHeight, &height );
244 HIRect splitterRect;
245 if (orient == wxVERTICAL)
246 splitterRect = CGRectMake( position, 0, height, size.y );
247 else
248 splitterRect = CGRectMake( 0, position, size.x, height );
249
250 // under compositing we should only draw when called by the OS, otherwise just issue a redraw command
251 // strange redraw errors occur if we don't do this
252
253 if ( !dc.IsKindOf( CLASSINFO( wxPaintDC ) ) )
254 {
255 wxRect rect( (int) splitterRect.origin.x, (int) splitterRect.origin.y, (int) splitterRect.size.width,
256 (int) splitterRect.size.height );
257 win->Refresh( &rect );
258 }
259 else
260 {
261 CGContextRef cgContext;
262 wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl();
263 cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext();
264
265 HIThemeSplitterDrawInfo drawInfo;
266 drawInfo.version = 0;
267 drawInfo.state = kThemeStateActive;
268 drawInfo.adornment = hasMetal ? kHIThemeSplitterAdornmentMetal : kHIThemeSplitterAdornmentNone;
269 HIThemeDrawPaneSplitter( &splitterRect, &drawInfo, cgContext, kHIThemeOrientationNormal );
270 }
271 }
272
273 void
274 wxRendererMac::DrawItemSelectionRect(wxWindow * WXUNUSED(win),
275 wxDC& dc,
276 const wxRect& rect,
277 int flags)
278 {
279 if ( !(flags & wxCONTROL_SELECTED) )
280 return;
281
282 wxColour col( wxMacCreateCGColorFromHITheme( (flags & wxCONTROL_FOCUSED) ?
283 kThemeBrushAlternatePrimaryHighlightColor
284 : kThemeBrushSecondaryHighlightColor ) );
285 wxBrush selBrush( col );
286
287 dc.SetPen( *wxTRANSPARENT_PEN );
288 dc.SetBrush( selBrush );
289 dc.DrawRectangle( rect );
290 }
291
292
293 void
294 wxRendererMac::DrawMacThemeButton(wxWindow *win,
295 wxDC& dc,
296 const wxRect& rect,
297 int flags,
298 int kind,
299 int adornment)
300 {
301 // now the wxGCDC is using native transformations
302 const wxCoord x = rect.x;
303 const wxCoord y = rect.y;
304 const wxCoord w = rect.width;
305 const wxCoord h = rect.height;
306
307 dc.SetBrush( *wxTRANSPARENT_BRUSH );
308
309 HIRect headerRect = CGRectMake( x, y, w, h );
310 if ( !dc.IsKindOf( CLASSINFO( wxPaintDC ) ) )
311 {
312 win->Refresh( &rect );
313 }
314 else
315 {
316 wxGCDCImpl *impl = (wxGCDCImpl*) dc.GetImpl();
317 CGContextRef cgContext;
318 cgContext = (CGContextRef) impl->GetGraphicsContext()->GetNativeContext();
319
320 HIThemeButtonDrawInfo drawInfo;
321 HIRect labelRect;
322
323 memset( &drawInfo, 0, sizeof(drawInfo) );
324 drawInfo.version = 0;
325 drawInfo.kind = kind;
326 drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive;
327 drawInfo.value = (flags & wxCONTROL_SELECTED) ? kThemeButtonOn : kThemeButtonOff;
328 if (flags & wxCONTROL_UNDETERMINED)
329 drawInfo.value = kThemeButtonMixed;
330 drawInfo.adornment = adornment;
331
332 HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect );
333 }
334 }
335
336 void
337 wxRendererMac::DrawCheckBox(wxWindow *win,
338 wxDC& dc,
339 const wxRect& rect,
340 int flags)
341 {
342 if (flags & wxCONTROL_CHECKED)
343 flags |= wxCONTROL_SELECTED;
344
345 DrawMacThemeButton(win, dc, rect, flags,
346 kThemeCheckBox, kThemeAdornmentNone);
347 }
348
349 wxSize wxRendererMac::GetCheckBoxSize(wxWindow* WXUNUSED(win))
350 {
351 wxSize size;
352 SInt32 width, height;
353 OSStatus errStatus;
354
355 errStatus = GetThemeMetric(kThemeMetricCheckBoxWidth, &width);
356 if (errStatus == noErr)
357 {
358 size.SetWidth(width);
359 }
360
361 errStatus = GetThemeMetric(kThemeMetricCheckBoxHeight, &height);
362 if (errStatus == noErr)
363 {
364 size.SetHeight(height);
365 }
366
367 return size;
368 }
369
370 void
371 wxRendererMac::DrawComboBoxDropButton(wxWindow *win,
372 wxDC& dc,
373 const wxRect& rect,
374 int flags)
375 {
376 int kind;
377 if (win->GetWindowVariant() == wxWINDOW_VARIANT_SMALL || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL))
378 kind = kThemeArrowButtonSmall;
379 else if (win->GetWindowVariant() == wxWINDOW_VARIANT_MINI || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI))
380 kind = kThemeArrowButtonMini;
381 else
382 kind = kThemeArrowButton;
383
384 DrawMacThemeButton(win, dc, rect, flags,
385 kind, kThemeAdornmentArrowDownArrow);
386 }
387
388 void
389 wxRendererMac::DrawPushButton(wxWindow *win,
390 wxDC& dc,
391 const wxRect& rect,
392 int flags)
393 {
394 int kind;
395 if (win->GetWindowVariant() == wxWINDOW_VARIANT_SMALL || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL))
396 kind = kThemeBevelButtonSmall;
397 // There is no kThemeBevelButtonMini, but in this case, use Small
398 else if (win->GetWindowVariant() == wxWINDOW_VARIANT_MINI || (win->GetParent() && win->GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI))
399 kind = kThemeBevelButtonSmall;
400 else
401 kind = kThemeBevelButton;
402
403 DrawMacThemeButton(win, dc, rect, flags,
404 kind, kThemeAdornmentNone);
405 }
406
407 void
408 wxRendererMac::DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
409 {
410 if (!win)
411 {
412 wxDelegateRendererNative::DrawFocusRect(win, dc, rect, flags);
413 return;
414 }
415
416 CGRect cgrect = CGRectMake( rect.x , rect.y , rect.width, rect.height ) ;
417
418 HIThemeFrameDrawInfo info ;
419 memset( &info, 0 , sizeof(info) ) ;
420
421 info.version = 0 ;
422 info.kind = 0 ;
423 info.state = kThemeStateActive;
424 info.isFocused = true ;
425
426 CGContextRef cgContext = (CGContextRef) win->MacGetCGContextRef() ;
427 wxASSERT( cgContext ) ;
428
429 HIThemeDrawFocusRect( &cgrect , true , cgContext , kHIThemeOrientationNormal ) ;
430 }