|
1 /* |
|
2 * This file is part of the theme implementation for form controls in WebCore. |
|
3 * |
|
4 * Copyright (C) 2005, 2006 Apple Computer, Inc. |
|
5 * |
|
6 * This library is free software; you can redistribute it and/or |
|
7 * modify it under the terms of the GNU Library General Public |
|
8 * License as published by the Free Software Foundation; either |
|
9 * version 2 of the License, or (at your option) any later version. |
|
10 * |
|
11 * This library is distributed in the hope that it will be useful, |
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 * Library General Public License for more details. |
|
15 * |
|
16 * You should have received a copy of the GNU Library General Public License |
|
17 * along with this library; see the file COPYING.LIB. If not, write to |
|
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
|
19 * Boston, MA 02110-1301, USA. |
|
20 */ |
|
21 |
|
22 #import "config.h" |
|
23 #import "RenderThemeMac.h" |
|
24 |
|
25 #import "CSSStyleSelector.h" |
|
26 #import "CSSValueKeywords.h" |
|
27 #import "Document.h" |
|
28 #import "Element.h" |
|
29 #import "FoundationExtras.h" |
|
30 #import "FrameView.h" |
|
31 #import "GraphicsContext.h" |
|
32 #import "HTMLInputElement.h" |
|
33 #import "Image.h" |
|
34 #import "LocalCurrentGraphicsContext.h" |
|
35 #import "RenderSlider.h" |
|
36 #import "RenderView.h" |
|
37 #import "WebCoreSystemInterface.h" |
|
38 #import <Cocoa/Cocoa.h> |
|
39 #import <wtf/RetainPtr.h> |
|
40 #import <math.h> |
|
41 |
|
42 using std::min; |
|
43 |
|
44 // The methods in this file are specific to the Mac OS X platform. |
|
45 |
|
46 // FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari. |
|
47 |
|
48 @interface WebCoreRenderThemeNotificationObserver : NSObject |
|
49 { |
|
50 WebCore::RenderTheme *_theme; |
|
51 } |
|
52 |
|
53 - (id)initWithTheme:(WebCore::RenderTheme *)theme; |
|
54 - (void)systemColorsDidChange:(NSNotification *)notification; |
|
55 |
|
56 @end |
|
57 |
|
58 @implementation WebCoreRenderThemeNotificationObserver |
|
59 |
|
60 - (id)initWithTheme:(WebCore::RenderTheme *)theme |
|
61 { |
|
62 [super init]; |
|
63 _theme = theme; |
|
64 |
|
65 return self; |
|
66 } |
|
67 |
|
68 - (void)systemColorsDidChange:(NSNotification *)notification |
|
69 { |
|
70 ASSERT([[notification name] isEqualToString:NSSystemColorsDidChangeNotification]); |
|
71 _theme->platformColorsDidChange(); |
|
72 } |
|
73 |
|
74 @end |
|
75 |
|
76 namespace WebCore { |
|
77 |
|
78 enum { |
|
79 topMargin, |
|
80 rightMargin, |
|
81 bottomMargin, |
|
82 leftMargin |
|
83 }; |
|
84 |
|
85 enum { |
|
86 topPadding, |
|
87 rightPadding, |
|
88 bottomPadding, |
|
89 leftPadding |
|
90 }; |
|
91 |
|
92 RenderTheme* theme() |
|
93 { |
|
94 static RenderThemeMac* macTheme = new RenderThemeMac; |
|
95 return macTheme; |
|
96 } |
|
97 |
|
98 RenderThemeMac::RenderThemeMac() |
|
99 : m_resizeCornerImage(0) |
|
100 , m_isSliderThumbHorizontalPressed(false) |
|
101 , m_isSliderThumbVerticalPressed(false) |
|
102 , m_notificationObserver(AdoptNS, [[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this]) |
|
103 { |
|
104 [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get() |
|
105 selector:@selector(systemColorsDidChange:) |
|
106 name:NSSystemColorsDidChangeNotification |
|
107 object:nil]; |
|
108 } |
|
109 |
|
110 RenderThemeMac::~RenderThemeMac() |
|
111 { |
|
112 [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()]; |
|
113 delete m_resizeCornerImage; |
|
114 } |
|
115 |
|
116 Color RenderThemeMac::platformActiveSelectionBackgroundColor() const |
|
117 { |
|
118 NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; |
|
119 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); |
|
120 } |
|
121 |
|
122 Color RenderThemeMac::platformInactiveSelectionBackgroundColor() const |
|
123 { |
|
124 NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; |
|
125 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); |
|
126 } |
|
127 |
|
128 Color RenderThemeMac::activeListBoxSelectionBackgroundColor() const |
|
129 { |
|
130 NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; |
|
131 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); |
|
132 } |
|
133 |
|
134 void RenderThemeMac::systemFont(int propId, FontDescription& fontDescription) const |
|
135 { |
|
136 static FontDescription systemFont; |
|
137 static FontDescription smallSystemFont; |
|
138 static FontDescription menuFont; |
|
139 static FontDescription labelFont; |
|
140 static FontDescription miniControlFont; |
|
141 static FontDescription smallControlFont; |
|
142 static FontDescription controlFont; |
|
143 |
|
144 FontDescription* cachedDesc; |
|
145 NSFont* font = nil; |
|
146 switch (propId) { |
|
147 case CSS_VAL_SMALL_CAPTION: |
|
148 cachedDesc = &smallSystemFont; |
|
149 if (!smallSystemFont.isAbsoluteSize()) |
|
150 font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; |
|
151 break; |
|
152 case CSS_VAL_MENU: |
|
153 cachedDesc = &menuFont; |
|
154 if (!menuFont.isAbsoluteSize()) |
|
155 font = [NSFont menuFontOfSize:[NSFont systemFontSize]]; |
|
156 break; |
|
157 case CSS_VAL_STATUS_BAR: |
|
158 cachedDesc = &labelFont; |
|
159 if (!labelFont.isAbsoluteSize()) |
|
160 font = [NSFont labelFontOfSize:[NSFont labelFontSize]]; |
|
161 break; |
|
162 case CSS_VAL__WEBKIT_MINI_CONTROL: |
|
163 cachedDesc = &miniControlFont; |
|
164 if (!miniControlFont.isAbsoluteSize()) |
|
165 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]; |
|
166 break; |
|
167 case CSS_VAL__WEBKIT_SMALL_CONTROL: |
|
168 cachedDesc = &smallControlFont; |
|
169 if (!smallControlFont.isAbsoluteSize()) |
|
170 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; |
|
171 break; |
|
172 case CSS_VAL__WEBKIT_CONTROL: |
|
173 cachedDesc = &controlFont; |
|
174 if (!controlFont.isAbsoluteSize()) |
|
175 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; |
|
176 break; |
|
177 default: |
|
178 cachedDesc = &systemFont; |
|
179 if (!systemFont.isAbsoluteSize()) |
|
180 font = [NSFont systemFontOfSize:[NSFont systemFontSize]]; |
|
181 } |
|
182 |
|
183 if (font) { |
|
184 cachedDesc->setIsAbsoluteSize(true); |
|
185 cachedDesc->setGenericFamily(FontDescription::NoFamily); |
|
186 cachedDesc->firstFamily().setFamily([font familyName]); |
|
187 cachedDesc->setSpecifiedSize([font pointSize]); |
|
188 NSFontTraitMask traits = [[NSFontManager sharedFontManager] traitsOfFont:font]; |
|
189 cachedDesc->setBold(traits & NSBoldFontMask); |
|
190 cachedDesc->setItalic(traits & NSItalicFontMask); |
|
191 } |
|
192 fontDescription = *cachedDesc; |
|
193 } |
|
194 |
|
195 bool RenderThemeMac::isControlStyled(const RenderStyle* style, const BorderData& border, |
|
196 const BackgroundLayer& background, const Color& backgroundColor) const |
|
197 { |
|
198 if (style->appearance() == TextFieldAppearance || style->appearance() == TextAreaAppearance || style->appearance() == ListboxAppearance) |
|
199 return style->border() != border; |
|
200 return RenderTheme::isControlStyled(style, border, background, backgroundColor); |
|
201 } |
|
202 |
|
203 void RenderThemeMac::paintResizeControl(GraphicsContext* c, const IntRect& r) |
|
204 { |
|
205 Image* resizeCornerImage = this->resizeCornerImage(); |
|
206 IntPoint imagePoint(r.right() - resizeCornerImage->width(), r.bottom() - resizeCornerImage->height()); |
|
207 c->drawImage(resizeCornerImage, imagePoint); |
|
208 } |
|
209 |
|
210 void RenderThemeMac::adjustRepaintRect(const RenderObject* o, IntRect& r) |
|
211 { |
|
212 switch (o->style()->appearance()) { |
|
213 case CheckboxAppearance: { |
|
214 // Since we query the prototype cell, we need to update its state to match. |
|
215 setCheckboxCellState(o, r); |
|
216 |
|
217 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox |
|
218 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. |
|
219 r = inflateRect(r, checkboxSizes()[[checkbox() controlSize]], checkboxMargins()); |
|
220 break; |
|
221 } |
|
222 case RadioAppearance: { |
|
223 // Since we query the prototype cell, we need to update its state to match. |
|
224 setRadioCellState(o, r); |
|
225 |
|
226 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox |
|
227 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. |
|
228 r = inflateRect(r, radioSizes()[[radio() controlSize]], radioMargins()); |
|
229 break; |
|
230 } |
|
231 case PushButtonAppearance: |
|
232 case ButtonAppearance: { |
|
233 // Since we query the prototype cell, we need to update its state to match. |
|
234 setButtonCellState(o, r); |
|
235 |
|
236 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox |
|
237 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. |
|
238 if ([button() bezelStyle] == NSRoundedBezelStyle) |
|
239 r = inflateRect(r, buttonSizes()[[button() controlSize]], buttonMargins()); |
|
240 break; |
|
241 } |
|
242 case MenulistAppearance: { |
|
243 setPopupButtonCellState(o, r); |
|
244 r = inflateRect(r, popupButtonSizes()[[popupButton() controlSize]], popupButtonMargins()); |
|
245 break; |
|
246 } |
|
247 default: |
|
248 break; |
|
249 } |
|
250 } |
|
251 |
|
252 IntRect RenderThemeMac::inflateRect(const IntRect& r, const IntSize& size, const int* margins) const |
|
253 { |
|
254 // Only do the inflation if the available width/height are too small. Otherwise try to |
|
255 // fit the glow/check space into the available box's width/height. |
|
256 int widthDelta = r.width() - (size.width() + margins[leftMargin] + margins[rightMargin]); |
|
257 int heightDelta = r.height() - (size.height() + margins[topMargin] + margins[bottomMargin]); |
|
258 IntRect result(r); |
|
259 if (widthDelta < 0) { |
|
260 result.setX(result.x() - margins[leftMargin]); |
|
261 result.setWidth(result.width() - widthDelta); |
|
262 } |
|
263 if (heightDelta < 0) { |
|
264 result.setY(result.y() - margins[topMargin]); |
|
265 result.setHeight(result.height() - heightDelta); |
|
266 } |
|
267 return result; |
|
268 } |
|
269 |
|
270 void RenderThemeMac::updateCheckedState(NSCell* cell, const RenderObject* o) |
|
271 { |
|
272 bool oldIndeterminate = [cell state] == NSMixedState; |
|
273 bool indeterminate = isIndeterminate(o); |
|
274 bool checked = isChecked(o); |
|
275 |
|
276 if (oldIndeterminate != indeterminate) { |
|
277 [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)]; |
|
278 return; |
|
279 } |
|
280 |
|
281 bool oldChecked = [cell state] == NSOnState; |
|
282 if (checked != oldChecked) |
|
283 [cell setState:checked ? NSOnState : NSOffState]; |
|
284 } |
|
285 |
|
286 void RenderThemeMac::updateEnabledState(NSCell* cell, const RenderObject* o) |
|
287 { |
|
288 bool oldEnabled = [cell isEnabled]; |
|
289 bool enabled = isEnabled(o); |
|
290 if (enabled != oldEnabled) |
|
291 [cell setEnabled:enabled]; |
|
292 } |
|
293 |
|
294 void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject* o) |
|
295 { |
|
296 bool oldFocused = [cell showsFirstResponder]; |
|
297 bool focused = isFocused(o) && o->style()->outlineStyleIsAuto(); |
|
298 if (focused != oldFocused) |
|
299 [cell setShowsFirstResponder:focused]; |
|
300 } |
|
301 |
|
302 void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject* o) |
|
303 { |
|
304 bool oldPressed = [cell isHighlighted]; |
|
305 bool pressed = (o->element() && o->element()->active()); |
|
306 if (pressed != oldPressed) |
|
307 [cell setHighlighted:pressed]; |
|
308 } |
|
309 |
|
310 short RenderThemeMac::baselinePosition(const RenderObject* o) const |
|
311 { |
|
312 if (o->style()->appearance() == CheckboxAppearance || o->style()->appearance() == RadioAppearance) |
|
313 return o->marginTop() + o->height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit. |
|
314 return RenderTheme::baselinePosition(o); |
|
315 } |
|
316 |
|
317 bool RenderThemeMac::controlSupportsTints(const RenderObject* o) const |
|
318 { |
|
319 // An alternate way to implement this would be to get the appropriate cell object |
|
320 // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of |
|
321 // that would be that we would match AppKit behavior more closely, but a disadvantage |
|
322 // would be that we would rely on an AppKit SPI method. |
|
323 |
|
324 if (!isEnabled(o)) |
|
325 return false; |
|
326 |
|
327 // Checkboxes only have tint when checked. |
|
328 if (o->style()->appearance() == CheckboxAppearance) |
|
329 return isChecked(o); |
|
330 |
|
331 // For now assume other controls have tint if enabled. |
|
332 return true; |
|
333 } |
|
334 |
|
335 NSControlSize RenderThemeMac::controlSizeForFont(RenderStyle* style) const |
|
336 { |
|
337 int fontSize = style->fontSize(); |
|
338 if (fontSize >= 16) |
|
339 return NSRegularControlSize; |
|
340 if (fontSize >= 11) |
|
341 return NSSmallControlSize; |
|
342 return NSMiniControlSize; |
|
343 } |
|
344 |
|
345 void RenderThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize) |
|
346 { |
|
347 NSControlSize size; |
|
348 if (minSize.width() >= sizes[NSRegularControlSize].width() && |
|
349 minSize.height() >= sizes[NSRegularControlSize].height()) |
|
350 size = NSRegularControlSize; |
|
351 else if (minSize.width() >= sizes[NSSmallControlSize].width() && |
|
352 minSize.height() >= sizes[NSSmallControlSize].height()) |
|
353 size = NSSmallControlSize; |
|
354 else |
|
355 size = NSMiniControlSize; |
|
356 if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same. |
|
357 [cell setControlSize:size]; |
|
358 } |
|
359 |
|
360 IntSize RenderThemeMac::sizeForFont(RenderStyle* style, const IntSize* sizes) const |
|
361 { |
|
362 return sizes[controlSizeForFont(style)]; |
|
363 } |
|
364 |
|
365 IntSize RenderThemeMac::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const |
|
366 { |
|
367 return sizes[controlSizeForSystemFont(style)]; |
|
368 } |
|
369 |
|
370 void RenderThemeMac::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const |
|
371 { |
|
372 // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. |
|
373 IntSize size = sizeForFont(style, sizes); |
|
374 if (style->width().isIntrinsicOrAuto() && size.width() > 0) |
|
375 style->setWidth(Length(size.width(), Fixed)); |
|
376 if (style->height().isAuto() && size.height() > 0) |
|
377 style->setHeight(Length(size.height(), Fixed)); |
|
378 } |
|
379 |
|
380 void RenderThemeMac::setFontFromControlSize(CSSStyleSelector* selector, RenderStyle* style, NSControlSize controlSize) const |
|
381 { |
|
382 FontDescription fontDescription; |
|
383 fontDescription.setIsAbsoluteSize(true); |
|
384 fontDescription.setGenericFamily(FontDescription::SerifFamily); |
|
385 |
|
386 NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]]; |
|
387 fontDescription.firstFamily().setFamily([font familyName]); |
|
388 fontDescription.setComputedSize([font pointSize]); |
|
389 fontDescription.setSpecifiedSize([font pointSize]); |
|
390 |
|
391 // Reset line height |
|
392 style->setLineHeight(RenderStyle::initialLineHeight()); |
|
393 |
|
394 if (style->setFontDescription(fontDescription)) |
|
395 style->font().update(); |
|
396 } |
|
397 |
|
398 NSControlSize RenderThemeMac::controlSizeForSystemFont(RenderStyle* style) const |
|
399 { |
|
400 int fontSize = style->fontSize(); |
|
401 if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize]) |
|
402 return NSRegularControlSize; |
|
403 if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize]) |
|
404 return NSSmallControlSize; |
|
405 return NSMiniControlSize; |
|
406 } |
|
407 |
|
408 bool RenderThemeMac::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r) |
|
409 { |
|
410 // Determine the width and height needed for the control and prepare the cell for painting. |
|
411 setCheckboxCellState(o, r); |
|
412 |
|
413 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox |
|
414 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. |
|
415 NSButtonCell* checkbox = this->checkbox(); |
|
416 IntRect inflatedRect = inflateRect(r, checkboxSizes()[[checkbox controlSize]], checkboxMargins()); |
|
417 [checkbox drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()]; |
|
418 [checkbox setControlView:nil]; |
|
419 |
|
420 return false; |
|
421 } |
|
422 |
|
423 const IntSize* RenderThemeMac::checkboxSizes() const |
|
424 { |
|
425 static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) }; |
|
426 return sizes; |
|
427 } |
|
428 |
|
429 const int* RenderThemeMac::checkboxMargins() const |
|
430 { |
|
431 static const int margins[3][4] = |
|
432 { |
|
433 { 3, 4, 4, 2 }, |
|
434 { 4, 3, 3, 3 }, |
|
435 { 4, 3, 3, 3 }, |
|
436 }; |
|
437 return margins[[checkbox() controlSize]]; |
|
438 } |
|
439 |
|
440 void RenderThemeMac::setCheckboxCellState(const RenderObject* o, const IntRect& r) |
|
441 { |
|
442 NSButtonCell* checkbox = this->checkbox(); |
|
443 |
|
444 // Set the control size based off the rectangle we're painting into. |
|
445 setControlSize(checkbox, checkboxSizes(), r.size()); |
|
446 |
|
447 // Update the various states we respond to. |
|
448 updateCheckedState(checkbox, o); |
|
449 updateEnabledState(checkbox, o); |
|
450 updatePressedState(checkbox, o); |
|
451 updateFocusedState(checkbox, o); |
|
452 } |
|
453 |
|
454 void RenderThemeMac::setCheckboxSize(RenderStyle* style) const |
|
455 { |
|
456 // If the width and height are both specified, then we have nothing to do. |
|
457 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) |
|
458 return; |
|
459 |
|
460 // Use the font size to determine the intrinsic width of the control. |
|
461 setSizeFromFont(style, checkboxSizes()); |
|
462 } |
|
463 |
|
464 bool RenderThemeMac::paintRadio(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r) |
|
465 { |
|
466 // Determine the width and height needed for the control and prepare the cell for painting. |
|
467 setRadioCellState(o, r); |
|
468 |
|
469 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox |
|
470 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. |
|
471 NSButtonCell* radio = this->radio(); |
|
472 IntRect inflatedRect = inflateRect(r, radioSizes()[[radio controlSize]], radioMargins()); |
|
473 [radio drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()]; |
|
474 [radio setControlView:nil]; |
|
475 |
|
476 return false; |
|
477 } |
|
478 |
|
479 const IntSize* RenderThemeMac::radioSizes() const |
|
480 { |
|
481 static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) }; |
|
482 return sizes; |
|
483 } |
|
484 |
|
485 const int* RenderThemeMac::radioMargins() const |
|
486 { |
|
487 static const int margins[3][4] = |
|
488 { |
|
489 { 2, 2, 4, 2 }, |
|
490 { 3, 2, 3, 2 }, |
|
491 { 1, 0, 2, 0 }, |
|
492 }; |
|
493 return margins[[radio() controlSize]]; |
|
494 } |
|
495 |
|
496 void RenderThemeMac::setRadioCellState(const RenderObject* o, const IntRect& r) |
|
497 { |
|
498 NSButtonCell* radio = this->radio(); |
|
499 |
|
500 // Set the control size based off the rectangle we're painting into. |
|
501 setControlSize(radio, radioSizes(), r.size()); |
|
502 |
|
503 // Update the various states we respond to. |
|
504 updateCheckedState(radio, o); |
|
505 updateEnabledState(radio, o); |
|
506 updatePressedState(radio, o); |
|
507 updateFocusedState(radio, o); |
|
508 } |
|
509 |
|
510 |
|
511 void RenderThemeMac::setRadioSize(RenderStyle* style) const |
|
512 { |
|
513 // If the width and height are both specified, then we have nothing to do. |
|
514 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) |
|
515 return; |
|
516 |
|
517 // Use the font size to determine the intrinsic width of the control. |
|
518 setSizeFromFont(style, radioSizes()); |
|
519 } |
|
520 |
|
521 void RenderThemeMac::setButtonPaddingFromControlSize(RenderStyle* style, NSControlSize size) const |
|
522 { |
|
523 // Just use 8px. AppKit wants to use 11px for mini buttons, but that padding is just too large |
|
524 // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is |
|
525 // by definition constrained, since we select mini only for small cramped environments. |
|
526 // This also guarantees the HTML4 <button> will match our rendering by default, since we're using a consistent |
|
527 // padding. |
|
528 const int padding = 8; |
|
529 style->setPaddingLeft(Length(padding, Fixed)); |
|
530 style->setPaddingRight(Length(padding, Fixed)); |
|
531 style->setPaddingTop(Length(0, Fixed)); |
|
532 style->setPaddingBottom(Length(0, Fixed)); |
|
533 } |
|
534 |
|
535 void RenderThemeMac::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
|
536 { |
|
537 // There are three appearance constants for buttons. |
|
538 // (1) Push-button is the constant for the default Aqua system button. Push buttons will not scale vertically and will not allow |
|
539 // custom fonts or colors. <input>s use this constant. This button will allow custom colors and font weights/variants but won't |
|
540 // scale vertically. |
|
541 // (2) square-button is the constant for the square button. This button will allow custom fonts and colors and will scale vertically. |
|
542 // (3) Button is the constant that means "pick the best button as appropriate." <button>s use this constant. This button will |
|
543 // also scale vertically and allow custom fonts and colors. It will attempt to use Aqua if possible and will make this determination |
|
544 // solely on the rectangle of the control. |
|
545 |
|
546 // Determine our control size based off our font. |
|
547 NSControlSize controlSize = controlSizeForFont(style); |
|
548 |
|
549 if (style->appearance() == PushButtonAppearance) { |
|
550 // Ditch the border. |
|
551 style->resetBorder(); |
|
552 |
|
553 // Height is locked to auto. |
|
554 style->setHeight(Length(Auto)); |
|
555 |
|
556 // White-space is locked to pre |
|
557 style->setWhiteSpace(PRE); |
|
558 |
|
559 // Set the button's vertical size. |
|
560 setButtonSize(style); |
|
561 |
|
562 // Add in the padding that we'd like to use. |
|
563 setButtonPaddingFromControlSize(style, controlSize); |
|
564 |
|
565 // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out |
|
566 // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate |
|
567 // system font for the control size instead. |
|
568 setFontFromControlSize(selector, style, controlSize); |
|
569 } else { |
|
570 // Set a min-height so that we can't get smaller than the mini button. |
|
571 style->setMinHeight(Length(15, Fixed)); |
|
572 |
|
573 // Reset the top and bottom borders. |
|
574 style->resetBorderTop(); |
|
575 style->resetBorderBottom(); |
|
576 } |
|
577 |
|
578 style->setBoxShadow(0); |
|
579 } |
|
580 |
|
581 const IntSize* RenderThemeMac::buttonSizes() const |
|
582 { |
|
583 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; |
|
584 return sizes; |
|
585 } |
|
586 |
|
587 const int* RenderThemeMac::buttonMargins() const |
|
588 { |
|
589 static const int margins[3][4] = |
|
590 { |
|
591 { 4, 6, 7, 6 }, |
|
592 { 4, 5, 6, 5 }, |
|
593 { 0, 1, 1, 1 }, |
|
594 }; |
|
595 return margins[[button() controlSize]]; |
|
596 } |
|
597 |
|
598 void RenderThemeMac::setButtonSize(RenderStyle* style) const |
|
599 { |
|
600 // If the width and height are both specified, then we have nothing to do. |
|
601 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) |
|
602 return; |
|
603 |
|
604 // Use the font size to determine the intrinsic width of the control. |
|
605 setSizeFromFont(style, buttonSizes()); |
|
606 } |
|
607 |
|
608 void RenderThemeMac::setButtonCellState(const RenderObject* o, const IntRect& r) |
|
609 { |
|
610 NSButtonCell* button = this->button(); |
|
611 |
|
612 // Set the control size based off the rectangle we're painting into. |
|
613 if (o->style()->appearance() == SquareButtonAppearance || |
|
614 r.height() > buttonSizes()[NSRegularControlSize].height()) { |
|
615 // Use the square button |
|
616 if ([button bezelStyle] != NSShadowlessSquareBezelStyle) |
|
617 [button setBezelStyle:NSShadowlessSquareBezelStyle]; |
|
618 } else if ([button bezelStyle] != NSRoundedBezelStyle) |
|
619 [button setBezelStyle:NSRoundedBezelStyle]; |
|
620 |
|
621 setControlSize(button, buttonSizes(), r.size()); |
|
622 |
|
623 // Update the various states we respond to. |
|
624 updateCheckedState(button, o); |
|
625 updateEnabledState(button, o); |
|
626 updatePressedState(button, o); |
|
627 updateFocusedState(button, o); |
|
628 } |
|
629 |
|
630 bool RenderThemeMac::paintButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
631 { |
|
632 NSButtonCell* button = this->button(); |
|
633 LocalCurrentGraphicsContext localContext(paintInfo.context); |
|
634 |
|
635 // Determine the width and height needed for the control and prepare the cell for painting. |
|
636 setButtonCellState(o, r); |
|
637 |
|
638 // We inflate the rect as needed to account for padding included in the cell to accommodate the button |
|
639 // shadow. We don't consider this part of the bounds of the control in WebKit. |
|
640 IntSize size = buttonSizes()[[button controlSize]]; |
|
641 size.setWidth(r.width()); |
|
642 IntRect inflatedRect = r; |
|
643 if ([button bezelStyle] == NSRoundedBezelStyle) { |
|
644 // Center the button within the available space. |
|
645 if (inflatedRect.height() > size.height()) { |
|
646 inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - size.height()) / 2); |
|
647 inflatedRect.setHeight(size.height()); |
|
648 } |
|
649 |
|
650 // Now inflate it to account for the shadow. |
|
651 inflatedRect = inflateRect(inflatedRect, size, buttonMargins()); |
|
652 } |
|
653 |
|
654 [button drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()]; |
|
655 [button setControlView:nil]; |
|
656 |
|
657 return false; |
|
658 } |
|
659 |
|
660 bool RenderThemeMac::paintTextField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
661 { |
|
662 LocalCurrentGraphicsContext localContext(paintInfo.context); |
|
663 wkDrawBezeledTextFieldCell(r, isEnabled(o) && !isReadOnlyControl(o)); |
|
664 return false; |
|
665 } |
|
666 |
|
667 void RenderThemeMac::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const |
|
668 { |
|
669 } |
|
670 |
|
671 bool RenderThemeMac::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
672 { |
|
673 LocalCurrentGraphicsContext localContext(paintInfo.context); |
|
674 wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o)); |
|
675 return false; |
|
676 } |
|
677 |
|
678 void RenderThemeMac::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const |
|
679 { |
|
680 } |
|
681 |
|
682 const int* RenderThemeMac::popupButtonMargins() const |
|
683 { |
|
684 static const int margins[3][4] = |
|
685 { |
|
686 { 0, 3, 1, 3 }, |
|
687 { 0, 3, 2, 3 }, |
|
688 { 0, 1, 0, 1 } |
|
689 }; |
|
690 return margins[[popupButton() controlSize]]; |
|
691 } |
|
692 |
|
693 const IntSize* RenderThemeMac::popupButtonSizes() const |
|
694 { |
|
695 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; |
|
696 return sizes; |
|
697 } |
|
698 |
|
699 const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const |
|
700 { |
|
701 static const int padding[3][4] = |
|
702 { |
|
703 { 2, 26, 3, 8 }, |
|
704 { 2, 23, 3, 8 }, |
|
705 { 2, 22, 3, 10 } |
|
706 }; |
|
707 return padding[size]; |
|
708 } |
|
709 |
|
710 bool RenderThemeMac::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
711 { |
|
712 setPopupButtonCellState(o, r); |
|
713 |
|
714 NSPopUpButtonCell* popupButton = this->popupButton(); |
|
715 |
|
716 IntRect inflatedRect = r; |
|
717 IntSize size = popupButtonSizes()[[popupButton controlSize]]; |
|
718 size.setWidth(r.width()); |
|
719 |
|
720 // Now inflate it to account for the shadow. |
|
721 if (r.width() >= minimumMenuListSize(o->style())) |
|
722 inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins()); |
|
723 |
|
724 #ifndef BUILDING_ON_TIGER |
|
725 // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect |
|
726 paintInfo.context->save(); |
|
727 paintInfo.context->clip(inflatedRect); |
|
728 #endif |
|
729 |
|
730 [popupButton drawWithFrame:inflatedRect inView:o->view()->frameView()->getDocumentView()]; |
|
731 [popupButton setControlView:nil]; |
|
732 |
|
733 #ifndef BUILDING_ON_TIGER |
|
734 paintInfo.context->restore(); |
|
735 #endif |
|
736 |
|
737 return false; |
|
738 } |
|
739 |
|
740 const float baseFontSize = 11.0f; |
|
741 const float baseArrowHeight = 4.0f; |
|
742 const float baseArrowWidth = 5.0f; |
|
743 const float baseSpaceBetweenArrows = 2.0f; |
|
744 const int arrowPaddingLeft = 6; |
|
745 const int arrowPaddingRight = 6; |
|
746 const int paddingBeforeSeparator = 4; |
|
747 const int baseBorderRadius = 5; |
|
748 const int styledPopupPaddingLeft = 8; |
|
749 const int styledPopupPaddingTop = 1; |
|
750 const int styledPopupPaddingBottom = 2; |
|
751 |
|
752 static void TopGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) |
|
753 { |
|
754 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f }; |
|
755 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f }; |
|
756 float a = inData[0]; |
|
757 int i = 0; |
|
758 for (i = 0; i < 4; i++) |
|
759 outData[i] = (1.0f - a) * dark[i] + a * light[i]; |
|
760 } |
|
761 |
|
762 static void BottomGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) |
|
763 { |
|
764 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f }; |
|
765 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f }; |
|
766 float a = inData[0]; |
|
767 int i = 0; |
|
768 for (i = 0; i < 4; i++) |
|
769 outData[i] = (1.0f - a) * dark[i] + a * light[i]; |
|
770 } |
|
771 |
|
772 static void MainGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) |
|
773 { |
|
774 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f }; |
|
775 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; |
|
776 float a = inData[0]; |
|
777 int i = 0; |
|
778 for (i = 0; i < 4; i++) |
|
779 outData[i] = (1.0f - a) * dark[i] + a * light[i]; |
|
780 } |
|
781 |
|
782 static void TrackGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) |
|
783 { |
|
784 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f }; |
|
785 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f }; |
|
786 float a = inData[0]; |
|
787 int i = 0; |
|
788 for (i = 0; i < 4; i++) |
|
789 outData[i] = (1.0f - a) * dark[i] + a * light[i]; |
|
790 } |
|
791 |
|
792 void RenderThemeMac::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
793 { |
|
794 CGContextRef context = paintInfo.context->platformContext(); |
|
795 |
|
796 paintInfo.context->save(); |
|
797 |
|
798 int radius = o->style()->borderTopLeftRadius().width(); |
|
799 |
|
800 RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB()); |
|
801 |
|
802 FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f); |
|
803 struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL }; |
|
804 RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks)); |
|
805 RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.bottom()), topFunction.get(), false, false)); |
|
806 |
|
807 FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f); |
|
808 struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL }; |
|
809 RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks)); |
|
810 RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.bottom()), bottomFunction.get(), false, false)); |
|
811 |
|
812 struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL }; |
|
813 RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); |
|
814 RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.bottom()), mainFunction.get(), false, false)); |
|
815 |
|
816 RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false)); |
|
817 |
|
818 RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.right(), r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false)); |
|
819 paintInfo.context->save(); |
|
820 CGContextClipToRect(context, r); |
|
821 paintInfo.context->addRoundedRectClip(r, |
|
822 o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), |
|
823 o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); |
|
824 CGContextDrawShading(context, mainShading.get()); |
|
825 paintInfo.context->restore(); |
|
826 |
|
827 paintInfo.context->save(); |
|
828 CGContextClipToRect(context, topGradient); |
|
829 paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient), |
|
830 o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), |
|
831 IntSize(), IntSize()); |
|
832 CGContextDrawShading(context, topShading.get()); |
|
833 paintInfo.context->restore(); |
|
834 |
|
835 paintInfo.context->save(); |
|
836 CGContextClipToRect(context, bottomGradient); |
|
837 paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient), |
|
838 IntSize(), IntSize(), |
|
839 o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); |
|
840 CGContextDrawShading(context, bottomShading.get()); |
|
841 paintInfo.context->restore(); |
|
842 |
|
843 paintInfo.context->save(); |
|
844 CGContextClipToRect(context, r); |
|
845 paintInfo.context->addRoundedRectClip(r, |
|
846 o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), |
|
847 o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); |
|
848 CGContextDrawShading(context, leftShading.get()); |
|
849 CGContextDrawShading(context, rightShading.get()); |
|
850 paintInfo.context->restore(); |
|
851 |
|
852 paintInfo.context->restore(); |
|
853 } |
|
854 |
|
855 bool RenderThemeMac::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
856 { |
|
857 paintInfo.context->save(); |
|
858 |
|
859 IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(), |
|
860 r.y() + o->style()->borderTopWidth(), |
|
861 r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(), |
|
862 r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth()); |
|
863 // Draw the gradients to give the styled popup menu a button appearance |
|
864 paintMenuListButtonGradients(o, paintInfo, bounds); |
|
865 |
|
866 // Since we actually know the size of the control here, we restrict the font scale to make sure the arrows will fit vertically in the bounds |
|
867 float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows)); |
|
868 float centerY = bounds.y() + bounds.height() / 2.0f; |
|
869 float arrowHeight = baseArrowHeight * fontScale; |
|
870 float arrowWidth = baseArrowWidth * fontScale; |
|
871 float leftEdge = bounds.right() - arrowPaddingRight - arrowWidth; |
|
872 float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale; |
|
873 |
|
874 if (bounds.width() < arrowWidth + arrowPaddingLeft) |
|
875 return false; |
|
876 |
|
877 paintInfo.context->setFillColor(o->style()->color()); |
|
878 paintInfo.context->setStrokeStyle(NoStroke); |
|
879 |
|
880 FloatPoint arrow1[3]; |
|
881 arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f); |
|
882 arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f); |
|
883 arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight); |
|
884 |
|
885 // Draw the top arrow |
|
886 paintInfo.context->drawConvexPolygon(3, arrow1, true); |
|
887 |
|
888 FloatPoint arrow2[3]; |
|
889 arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f); |
|
890 arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f); |
|
891 arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight); |
|
892 |
|
893 // Draw the bottom arrow |
|
894 paintInfo.context->drawConvexPolygon(3, arrow2, true); |
|
895 |
|
896 Color leftSeparatorColor(0, 0, 0, 40); |
|
897 Color rightSeparatorColor(255, 255, 255, 40); |
|
898 |
|
899 // FIXME: Should the separator thickness and space be scaled up by fontScale? |
|
900 int separatorSpace = 2; |
|
901 int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft); // FIXME: Round? |
|
902 |
|
903 // Draw the separator to the left of the arrows |
|
904 paintInfo.context->setStrokeThickness(1.0f); |
|
905 paintInfo.context->setStrokeStyle(SolidStroke); |
|
906 paintInfo.context->setStrokeColor(leftSeparatorColor); |
|
907 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), |
|
908 IntPoint(leftEdgeOfSeparator, bounds.bottom())); |
|
909 |
|
910 paintInfo.context->setStrokeColor(rightSeparatorColor); |
|
911 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), |
|
912 IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom())); |
|
913 |
|
914 paintInfo.context->restore(); |
|
915 return false; |
|
916 } |
|
917 |
|
918 void RenderThemeMac::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
|
919 { |
|
920 NSControlSize controlSize = controlSizeForFont(style); |
|
921 |
|
922 style->resetBorder(); |
|
923 style->resetPadding(); |
|
924 |
|
925 // Height is locked to auto. |
|
926 style->setHeight(Length(Auto)); |
|
927 |
|
928 // White-space is locked to pre |
|
929 style->setWhiteSpace(PRE); |
|
930 |
|
931 // Set the foreground color to black or gray when we have the aqua look. |
|
932 // Cast to RGB32 is to work around a compiler bug. |
|
933 style->setColor(e->isEnabled() ? static_cast<RGBA32>(Color::black) : Color::darkGray); |
|
934 |
|
935 // Set the button's vertical size. |
|
936 setButtonSize(style); |
|
937 |
|
938 // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out |
|
939 // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate |
|
940 // system font for the control size instead. |
|
941 setFontFromControlSize(selector, style, controlSize); |
|
942 |
|
943 style->setBoxShadow(0); |
|
944 } |
|
945 |
|
946 int RenderThemeMac::popupInternalPaddingLeft(RenderStyle* style) const |
|
947 { |
|
948 if (style->appearance() == MenulistAppearance) |
|
949 return popupButtonPadding(controlSizeForFont(style))[leftPadding]; |
|
950 if (style->appearance() == MenulistButtonAppearance) |
|
951 return styledPopupPaddingLeft; |
|
952 return 0; |
|
953 } |
|
954 |
|
955 int RenderThemeMac::popupInternalPaddingRight(RenderStyle* style) const |
|
956 { |
|
957 if (style->appearance() == MenulistAppearance) |
|
958 return popupButtonPadding(controlSizeForFont(style))[rightPadding]; |
|
959 if (style->appearance() == MenulistButtonAppearance) { |
|
960 float fontScale = style->fontSize() / baseFontSize; |
|
961 float arrowWidth = baseArrowWidth * fontScale; |
|
962 return static_cast<int>(ceilf(arrowWidth + arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator)); |
|
963 } |
|
964 return 0; |
|
965 } |
|
966 |
|
967 int RenderThemeMac::popupInternalPaddingTop(RenderStyle* style) const |
|
968 { |
|
969 if (style->appearance() == MenulistAppearance) |
|
970 return popupButtonPadding(controlSizeForFont(style))[topPadding]; |
|
971 if (style->appearance() == MenulistButtonAppearance) |
|
972 return styledPopupPaddingTop; |
|
973 return 0; |
|
974 } |
|
975 |
|
976 int RenderThemeMac::popupInternalPaddingBottom(RenderStyle* style) const |
|
977 { |
|
978 if (style->appearance() == MenulistAppearance) |
|
979 return popupButtonPadding(controlSizeForFont(style))[bottomPadding]; |
|
980 if (style->appearance() == MenulistButtonAppearance) |
|
981 return styledPopupPaddingBottom; |
|
982 return 0; |
|
983 } |
|
984 |
|
985 void RenderThemeMac::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
|
986 { |
|
987 float fontScale = style->fontSize() / baseFontSize; |
|
988 |
|
989 style->resetPadding(); |
|
990 style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up? |
|
991 |
|
992 const int minHeight = 15; |
|
993 style->setMinHeight(Length(minHeight, Fixed)); |
|
994 |
|
995 style->setLineHeight(RenderStyle::initialLineHeight()); |
|
996 } |
|
997 |
|
998 void RenderThemeMac::setPopupButtonCellState(const RenderObject* o, const IntRect& r) |
|
999 { |
|
1000 NSPopUpButtonCell* popupButton = this->popupButton(); |
|
1001 |
|
1002 // Set the control size based off the rectangle we're painting into. |
|
1003 setControlSize(popupButton, popupButtonSizes(), r.size()); |
|
1004 |
|
1005 // Update the various states we respond to. |
|
1006 updateCheckedState(popupButton, o); |
|
1007 updateEnabledState(popupButton, o); |
|
1008 updatePressedState(popupButton, o); |
|
1009 updateFocusedState(popupButton, o); |
|
1010 } |
|
1011 |
|
1012 const IntSize* RenderThemeMac::menuListSizes() const |
|
1013 { |
|
1014 static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) }; |
|
1015 return sizes; |
|
1016 } |
|
1017 |
|
1018 int RenderThemeMac::minimumMenuListSize(RenderStyle* style) const |
|
1019 { |
|
1020 return sizeForSystemFont(style, menuListSizes()).width(); |
|
1021 } |
|
1022 |
|
1023 const int trackWidth = 5; |
|
1024 const int trackRadius = 2; |
|
1025 |
|
1026 void RenderThemeMac::adjustSliderTrackStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
|
1027 { |
|
1028 style->setBoxShadow(0); |
|
1029 } |
|
1030 |
|
1031 bool RenderThemeMac::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
1032 { |
|
1033 IntRect bounds = r; |
|
1034 |
|
1035 if (o->style()->appearance() == SliderHorizontalAppearance) { |
|
1036 bounds.setHeight(trackWidth); |
|
1037 bounds.setY(r.y() + r.height() / 2 - trackWidth / 2); |
|
1038 } else if (o->style()->appearance() == SliderVerticalAppearance) { |
|
1039 bounds.setWidth(trackWidth); |
|
1040 bounds.setX(r.x() + r.width() / 2 - trackWidth / 2); |
|
1041 } |
|
1042 |
|
1043 LocalCurrentGraphicsContext localContext(paintInfo.context); |
|
1044 CGContextRef context = paintInfo.context->platformContext(); |
|
1045 RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB()); |
|
1046 |
|
1047 paintInfo.context->save(); |
|
1048 CGContextClipToRect(context, bounds); |
|
1049 |
|
1050 struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL }; |
|
1051 RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); |
|
1052 RetainPtr<CGShadingRef> mainShading; |
|
1053 if (o->style()->appearance() == SliderVerticalAppearance) |
|
1054 mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(), bounds.bottom()), CGPointMake(bounds.right(), bounds.bottom()), mainFunction.get(), false, false)); |
|
1055 else |
|
1056 mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(), bounds.y()), CGPointMake(bounds.x(), bounds.bottom()), mainFunction.get(), false, false)); |
|
1057 |
|
1058 IntSize radius(trackRadius, trackRadius); |
|
1059 paintInfo.context->addRoundedRectClip(bounds, |
|
1060 radius, radius, |
|
1061 radius, radius); |
|
1062 CGContextDrawShading(context, mainShading.get()); |
|
1063 paintInfo.context->restore(); |
|
1064 |
|
1065 return false; |
|
1066 } |
|
1067 |
|
1068 void RenderThemeMac::adjustSliderThumbStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
|
1069 { |
|
1070 style->setBoxShadow(0); |
|
1071 } |
|
1072 |
|
1073 const float verticalSliderHeightPadding = 0.1f; |
|
1074 |
|
1075 bool RenderThemeMac::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
1076 { |
|
1077 ASSERT(o->parent()->isSlider()); |
|
1078 |
|
1079 NSSliderCell* sliderThumbCell = o->style()->appearance() == SliderThumbVerticalAppearance |
|
1080 ? sliderThumbVertical() |
|
1081 : sliderThumbHorizontal(); |
|
1082 |
|
1083 LocalCurrentGraphicsContext localContext(paintInfo.context); |
|
1084 |
|
1085 // Update the various states we respond to. |
|
1086 updateEnabledState(sliderThumbCell, o->parent()); |
|
1087 updateFocusedState(sliderThumbCell, o->parent()); |
|
1088 |
|
1089 // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it. |
|
1090 bool oldPressed; |
|
1091 if (o->style()->appearance() == SliderThumbVerticalAppearance) |
|
1092 oldPressed = m_isSliderThumbVerticalPressed; |
|
1093 else |
|
1094 oldPressed = m_isSliderThumbHorizontalPressed; |
|
1095 |
|
1096 bool pressed = static_cast<RenderSlider*>(o->parent())->inDragMode(); |
|
1097 |
|
1098 if (o->style()->appearance() == SliderThumbVerticalAppearance) |
|
1099 m_isSliderThumbVerticalPressed = pressed; |
|
1100 else |
|
1101 m_isSliderThumbHorizontalPressed = pressed; |
|
1102 |
|
1103 if (pressed != oldPressed) { |
|
1104 if (pressed) |
|
1105 [sliderThumbCell startTrackingAt:NSPoint() inView:nil]; |
|
1106 else |
|
1107 [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES]; |
|
1108 } |
|
1109 |
|
1110 FloatRect bounds = r; |
|
1111 // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider. |
|
1112 if (o->style()->appearance() == SliderThumbVerticalAppearance) |
|
1113 bounds.setHeight(bounds.height() + verticalSliderHeightPadding); |
|
1114 |
|
1115 [sliderThumbCell drawWithFrame:NSRect(bounds) inView:o->view()->frameView()->getDocumentView()]; |
|
1116 [sliderThumbCell setControlView:nil]; |
|
1117 |
|
1118 return false; |
|
1119 } |
|
1120 |
|
1121 const int sliderThumbWidth = 15; |
|
1122 const int sliderThumbHeight = 15; |
|
1123 |
|
1124 void RenderThemeMac::adjustSliderThumbSize(RenderObject* o) const |
|
1125 { |
|
1126 if (o->style()->appearance() == SliderThumbHorizontalAppearance || o->style()->appearance() == SliderThumbVerticalAppearance) { |
|
1127 o->style()->setWidth(Length(sliderThumbWidth, Fixed)); |
|
1128 o->style()->setHeight(Length(sliderThumbHeight, Fixed)); |
|
1129 } |
|
1130 } |
|
1131 |
|
1132 bool RenderThemeMac::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
1133 { |
|
1134 NSSearchFieldCell* search = this->search(); |
|
1135 LocalCurrentGraphicsContext localContext(paintInfo.context); |
|
1136 |
|
1137 setSearchCellState(o, r); |
|
1138 |
|
1139 // Set the search button to nil before drawing. Then reset it so we can draw it later. |
|
1140 [search setSearchButtonCell:nil]; |
|
1141 |
|
1142 [search drawWithFrame:NSRect(r) inView:o->view()->frameView()->getDocumentView()]; |
|
1143 #ifdef BUILDING_ON_TIGER |
|
1144 if ([search showsFirstResponder]) |
|
1145 wkDrawTextFieldCellFocusRing(search, NSRect(r)); |
|
1146 #endif |
|
1147 |
|
1148 [search setControlView:nil]; |
|
1149 [search resetSearchButtonCell]; |
|
1150 |
|
1151 return false; |
|
1152 } |
|
1153 |
|
1154 void RenderThemeMac::setSearchCellState(RenderObject* o, const IntRect& r) |
|
1155 { |
|
1156 NSSearchFieldCell* search = this->search(); |
|
1157 |
|
1158 [search setControlSize:controlSizeForFont(o->style())]; |
|
1159 |
|
1160 // Update the various states we respond to. |
|
1161 updateEnabledState(search, o); |
|
1162 updateFocusedState(search, o); |
|
1163 } |
|
1164 |
|
1165 const IntSize* RenderThemeMac::searchFieldSizes() const |
|
1166 { |
|
1167 static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) }; |
|
1168 return sizes; |
|
1169 } |
|
1170 |
|
1171 void RenderThemeMac::setSearchFieldSize(RenderStyle* style) const |
|
1172 { |
|
1173 // If the width and height are both specified, then we have nothing to do. |
|
1174 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) |
|
1175 return; |
|
1176 |
|
1177 // Use the font size to determine the intrinsic width of the control. |
|
1178 setSizeFromFont(style, searchFieldSizes()); |
|
1179 } |
|
1180 |
|
1181 void RenderThemeMac::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
|
1182 { |
|
1183 // Override border. |
|
1184 style->resetBorder(); |
|
1185 const short borderWidth = 2; |
|
1186 style->setBorderLeftWidth(borderWidth); |
|
1187 style->setBorderLeftStyle(INSET); |
|
1188 style->setBorderRightWidth(borderWidth); |
|
1189 style->setBorderRightStyle(INSET); |
|
1190 style->setBorderBottomWidth(borderWidth); |
|
1191 style->setBorderBottomStyle(INSET); |
|
1192 style->setBorderTopWidth(borderWidth); |
|
1193 style->setBorderTopStyle(INSET); |
|
1194 |
|
1195 // Override height. |
|
1196 style->setHeight(Length(Auto)); |
|
1197 setSearchFieldSize(style); |
|
1198 |
|
1199 // Override padding size to match AppKit text positioning. |
|
1200 const int padding = 1; |
|
1201 style->setPaddingLeft(Length(padding, Fixed)); |
|
1202 style->setPaddingRight(Length(padding, Fixed)); |
|
1203 style->setPaddingTop(Length(padding, Fixed)); |
|
1204 style->setPaddingBottom(Length(padding, Fixed)); |
|
1205 |
|
1206 NSControlSize controlSize = controlSizeForFont(style); |
|
1207 setFontFromControlSize(selector, style, controlSize); |
|
1208 |
|
1209 style->setBoxShadow(0); |
|
1210 } |
|
1211 |
|
1212 bool RenderThemeMac::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
1213 { |
|
1214 Node* input = o->node()->shadowAncestorNode(); |
|
1215 setSearchCellState(input->renderer(), r); |
|
1216 |
|
1217 NSSearchFieldCell* search = this->search(); |
|
1218 |
|
1219 updatePressedState([search cancelButtonCell], o); |
|
1220 |
|
1221 NSRect bounds = [search cancelButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())]; |
|
1222 [[search cancelButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()]; |
|
1223 [[search cancelButtonCell] setControlView:nil]; |
|
1224 |
|
1225 return false; |
|
1226 } |
|
1227 |
|
1228 const IntSize* RenderThemeMac::cancelButtonSizes() const |
|
1229 { |
|
1230 static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) }; |
|
1231 return sizes; |
|
1232 } |
|
1233 |
|
1234 void RenderThemeMac::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
|
1235 { |
|
1236 IntSize size = sizeForSystemFont(style, cancelButtonSizes()); |
|
1237 style->setWidth(Length(size.width(), Fixed)); |
|
1238 style->setHeight(Length(size.height(), Fixed)); |
|
1239 style->setBoxShadow(0); |
|
1240 } |
|
1241 |
|
1242 const IntSize* RenderThemeMac::resultsButtonSizes() const |
|
1243 { |
|
1244 static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) }; |
|
1245 return sizes; |
|
1246 } |
|
1247 |
|
1248 const int emptyResultsOffset = 9; |
|
1249 void RenderThemeMac::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
|
1250 { |
|
1251 IntSize size = sizeForSystemFont(style, resultsButtonSizes()); |
|
1252 style->setWidth(Length(size.width() - emptyResultsOffset, Fixed)); |
|
1253 style->setHeight(Length(size.height(), Fixed)); |
|
1254 style->setBoxShadow(0); |
|
1255 } |
|
1256 |
|
1257 bool RenderThemeMac::paintSearchFieldDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
1258 { |
|
1259 return false; |
|
1260 } |
|
1261 |
|
1262 void RenderThemeMac::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
|
1263 { |
|
1264 IntSize size = sizeForSystemFont(style, resultsButtonSizes()); |
|
1265 style->setWidth(Length(size.width(), Fixed)); |
|
1266 style->setHeight(Length(size.height(), Fixed)); |
|
1267 style->setBoxShadow(0); |
|
1268 } |
|
1269 |
|
1270 bool RenderThemeMac::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
1271 { |
|
1272 Node* input = o->node()->shadowAncestorNode(); |
|
1273 setSearchCellState(input->renderer(), r); |
|
1274 |
|
1275 NSSearchFieldCell* search = this->search(); |
|
1276 |
|
1277 if ([search searchMenuTemplate] != nil) |
|
1278 [search setSearchMenuTemplate:nil]; |
|
1279 |
|
1280 NSRect bounds = [search searchButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())]; |
|
1281 [[search searchButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()]; |
|
1282 [[search searchButtonCell] setControlView:nil]; |
|
1283 return false; |
|
1284 } |
|
1285 |
|
1286 const int resultsArrowWidth = 5; |
|
1287 void RenderThemeMac::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
|
1288 { |
|
1289 IntSize size = sizeForSystemFont(style, resultsButtonSizes()); |
|
1290 style->setWidth(Length(size.width() + resultsArrowWidth, Fixed)); |
|
1291 style->setHeight(Length(size.height(), Fixed)); |
|
1292 style->setBoxShadow(0); |
|
1293 } |
|
1294 |
|
1295 bool RenderThemeMac::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) |
|
1296 { |
|
1297 Node* input = o->node()->shadowAncestorNode(); |
|
1298 setSearchCellState(input->renderer(), r); |
|
1299 |
|
1300 NSSearchFieldCell* search = this->search(); |
|
1301 |
|
1302 if (![search searchMenuTemplate]) |
|
1303 [search setSearchMenuTemplate:searchMenuTemplate()]; |
|
1304 |
|
1305 NSRect bounds = [search searchButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())]; |
|
1306 [[search searchButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()]; |
|
1307 [[search searchButtonCell] setControlView:nil]; |
|
1308 return false; |
|
1309 } |
|
1310 |
|
1311 NSButtonCell* RenderThemeMac::checkbox() const |
|
1312 { |
|
1313 if (!m_checkbox) { |
|
1314 m_checkbox.adoptNS([[NSButtonCell alloc] init]); |
|
1315 [m_checkbox.get() setButtonType:NSSwitchButton]; |
|
1316 [m_checkbox.get() setTitle:nil]; |
|
1317 [m_checkbox.get() setAllowsMixedState:YES]; |
|
1318 [m_checkbox.get() setFocusRingType:NSFocusRingTypeExterior]; |
|
1319 } |
|
1320 |
|
1321 return m_checkbox.get(); |
|
1322 } |
|
1323 |
|
1324 NSButtonCell* RenderThemeMac::radio() const |
|
1325 { |
|
1326 if (!m_radio) { |
|
1327 m_radio.adoptNS([[NSButtonCell alloc] init]); |
|
1328 [m_radio.get() setButtonType:NSRadioButton]; |
|
1329 [m_radio.get() setTitle:nil]; |
|
1330 [m_radio.get() setFocusRingType:NSFocusRingTypeExterior]; |
|
1331 } |
|
1332 |
|
1333 return m_radio.get(); |
|
1334 } |
|
1335 |
|
1336 NSButtonCell* RenderThemeMac::button() const |
|
1337 { |
|
1338 if (!m_button) { |
|
1339 m_button.adoptNS([[NSButtonCell alloc] init]); |
|
1340 [m_button.get() setTitle:nil]; |
|
1341 [m_button.get() setButtonType:NSMomentaryPushInButton]; |
|
1342 } |
|
1343 |
|
1344 return m_button.get(); |
|
1345 } |
|
1346 |
|
1347 NSPopUpButtonCell* RenderThemeMac::popupButton() const |
|
1348 { |
|
1349 if (!m_popupButton) { |
|
1350 m_popupButton.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]); |
|
1351 [m_popupButton.get() setUsesItemFromMenu:NO]; |
|
1352 [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior]; |
|
1353 } |
|
1354 |
|
1355 return m_popupButton.get(); |
|
1356 } |
|
1357 |
|
1358 NSSearchFieldCell* RenderThemeMac::search() const |
|
1359 { |
|
1360 if (!m_search) { |
|
1361 m_search.adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]); |
|
1362 [m_search.get() setBezelStyle:NSTextFieldRoundedBezel]; |
|
1363 [m_search.get() setBezeled:YES]; |
|
1364 [m_search.get() setEditable:YES]; |
|
1365 [m_search.get() setFocusRingType:NSFocusRingTypeExterior]; |
|
1366 } |
|
1367 |
|
1368 return m_search.get(); |
|
1369 } |
|
1370 |
|
1371 NSMenu* RenderThemeMac::searchMenuTemplate() const |
|
1372 { |
|
1373 if (!m_searchMenuTemplate) |
|
1374 m_searchMenuTemplate.adoptNS([[NSMenu alloc] initWithTitle:@""]); |
|
1375 |
|
1376 return m_searchMenuTemplate.get(); |
|
1377 } |
|
1378 |
|
1379 NSSliderCell* RenderThemeMac::sliderThumbHorizontal() const |
|
1380 { |
|
1381 if (!m_sliderThumbHorizontal) { |
|
1382 m_sliderThumbHorizontal.adoptNS([[NSSliderCell alloc] init]); |
|
1383 [m_sliderThumbHorizontal.get() setTitle:nil]; |
|
1384 [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider]; |
|
1385 [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize]; |
|
1386 [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior]; |
|
1387 } |
|
1388 |
|
1389 return m_sliderThumbHorizontal.get(); |
|
1390 } |
|
1391 |
|
1392 NSSliderCell* RenderThemeMac::sliderThumbVertical() const |
|
1393 { |
|
1394 if (!m_sliderThumbVertical) { |
|
1395 m_sliderThumbVertical.adoptNS([[NSSliderCell alloc] init]); |
|
1396 [m_sliderThumbVertical.get() setTitle:nil]; |
|
1397 [m_sliderThumbVertical.get() setSliderType:NSLinearSlider]; |
|
1398 [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize]; |
|
1399 [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior]; |
|
1400 } |
|
1401 |
|
1402 return m_sliderThumbVertical.get(); |
|
1403 } |
|
1404 |
|
1405 Image* RenderThemeMac::resizeCornerImage() const |
|
1406 { |
|
1407 if (!m_resizeCornerImage) |
|
1408 m_resizeCornerImage = Image::loadPlatformResource("textAreaResizeCorner"); |
|
1409 return m_resizeCornerImage; |
|
1410 } |
|
1411 |
|
1412 } // namespace WebCore |