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