OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. | 2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. |
3 * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmo bile.com/) | 3 * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmo bile.com/) |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * | 8 * |
9 * 1. Redistributions of source code must retain the above copyright | 9 * 1. Redistributions of source code must retain the above copyright |
10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
55 #include "platform/fonts/FontCache.h" | 55 #include "platform/fonts/FontCache.h" |
56 #include "platform/graphics/GraphicsContext.h" | 56 #include "platform/graphics/GraphicsContext.h" |
57 #include "platform/scroll/Scrollbar.h" | 57 #include "platform/scroll/Scrollbar.h" |
58 #include "platform/text/BidiTextRun.h" | 58 #include "platform/text/BidiTextRun.h" |
59 #include <math.h> | 59 #include <math.h> |
60 | 60 |
61 namespace WebCore { | 61 namespace WebCore { |
62 | 62 |
63 using namespace HTMLNames; | 63 using namespace HTMLNames; |
64 | 64 |
65 const int rowSpacing = 1; | |
66 | |
67 const int optionsSpacingHorizontal = 2; | |
68 | |
69 // The minSize constant was originally defined to render scrollbars correctly. | 65 // The minSize constant was originally defined to render scrollbars correctly. |
70 // This might vary for different platforms. | 66 // This might vary for different platforms. |
71 const int minSize = 4; | 67 const int minSize = 4; |
72 | 68 |
73 // Default size when the multiple attribute is present but size attribute is abs ent. | 69 // Default size when the multiple attribute is present but size attribute is abs ent. |
74 const int defaultSize = 4; | 70 const int defaultSize = 4; |
75 | 71 |
76 // FIXME: This hardcoded baselineAdjustment is what we used to do for the old | 72 // FIXME: This hardcoded baselineAdjustment is what we used to do for the old |
77 // widget, but I'm not sure this is right for the new control. | 73 // widget, but I'm not sure this is right for the new control. |
78 const int baselineAdjustment = 7; | 74 const int baselineAdjustment = 7; |
79 | 75 |
80 RenderListBox::RenderListBox(Element* element) | 76 RenderListBox::RenderListBox(Element* element) |
81 : RenderBlockFlow(element) | 77 : RenderBlockFlow(element) |
82 , m_optionsChanged(true) | |
83 , m_scrollToRevealSelectionAfterLayout(true) | |
84 , m_inAutoscroll(false) | |
85 , m_optionsWidth(0) | |
86 , m_indexOffset(0) | |
87 , m_listItemCount(0) | |
88 { | 78 { |
89 ASSERT(element); | 79 ASSERT(element); |
90 ASSERT(element->isHTMLElement()); | 80 ASSERT(element->isHTMLElement()); |
91 ASSERT(isHTMLSelectElement(element)); | 81 ASSERT(isHTMLSelectElement(element)); |
92 | |
93 if (FrameView* frameView = frame()->view()) | |
94 frameView->addScrollableArea(this); | |
95 } | 82 } |
96 | 83 |
97 RenderListBox::~RenderListBox() | 84 RenderListBox::~RenderListBox() |
98 { | 85 { |
99 setHasVerticalScrollbar(false); | |
100 | |
101 if (FrameView* frameView = frame()->view()) | |
102 frameView->removeScrollableArea(this); | |
103 } | |
104 | |
105 // FIXME: Instead of this hack we should add a ShadowRoot to <select> with no in sertion point | |
106 // to prevent children from rendering. | |
107 bool RenderListBox::isChildAllowed(RenderObject* object, RenderStyle*) const | |
108 { | |
109 return object->isAnonymous() && !object->isRenderFullScreen(); | |
110 } | 86 } |
111 | 87 |
112 inline HTMLSelectElement* RenderListBox::selectElement() const | 88 inline HTMLSelectElement* RenderListBox::selectElement() const |
113 { | 89 { |
114 return toHTMLSelectElement(node()); | 90 return toHTMLSelectElement(node()); |
115 } | 91 } |
116 | 92 |
117 void RenderListBox::updateFromElement() | |
118 { | |
119 FontCachePurgePreventer fontCachePurgePreventer; | |
120 if (m_optionsChanged) { | |
121 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = se lectElement()->listItems(); | |
122 int size = static_cast<int>(listItems.size()); | |
123 | |
124 float width = 0; | |
125 m_listItemCount = 0; | |
126 for (int i = 0; i < size; ++i) { | |
127 const HTMLElement& element = *listItems[i]; | |
128 | |
129 String text; | |
130 Font itemFont = style()->font(); | |
131 if (isHTMLOptionElement(element)) { | |
132 const HTMLOptionElement& optionElement = toHTMLOptionElement(ele ment); | |
133 if (optionElement.isDisplayNone()) | |
134 continue; | |
135 text = optionElement.textIndentedToRespectGroupLabel(); | |
136 ++m_listItemCount; | |
137 } else if (isHTMLOptGroupElement(element)) { | |
138 if (toHTMLOptGroupElement(element).isDisplayNone()) | |
139 continue; | |
140 text = toHTMLOptGroupElement(element).groupLabelText(); | |
141 FontDescription d = itemFont.fontDescription(); | |
142 d.setWeight(d.bolderWeight()); | |
143 itemFont = Font(d); | |
144 itemFont.update(document().styleEngine()->fontSelector()); | |
145 ++m_listItemCount; | |
146 } else if (isHTMLHRElement(element)) { | |
147 // HTMLSelect adds it to its list, so we will also add it to mat ch the count. | |
148 ++m_listItemCount; | |
149 continue; | |
150 } | |
151 | |
152 if (!text.isEmpty()) { | |
153 applyTextTransform(style(), text, ' '); | |
154 | |
155 bool hasStrongDirectionality; | |
156 TextDirection direction = determineDirectionality(text, hasStron gDirectionality); | |
157 TextRun textRun = constructTextRun(this, itemFont, text, style() , TextRun::AllowTrailingExpansion); | |
158 if (hasStrongDirectionality) | |
159 textRun.setDirection(direction); | |
160 textRun.disableRoundingHacks(); | |
161 float textWidth = itemFont.width(textRun); | |
162 width = std::max(width, textWidth); | |
163 } | |
164 } | |
165 m_optionsWidth = static_cast<int>(ceilf(width)); | |
166 m_optionsChanged = false; | |
167 | |
168 setHasVerticalScrollbar(true); | |
169 | |
170 setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); | |
171 } | |
172 } | |
173 | |
174 void RenderListBox::selectionChanged() | |
175 { | |
176 paintInvalidationForWholeRenderer(); | |
177 if (!m_inAutoscroll) { | |
178 if (m_optionsChanged || needsLayout()) | |
179 m_scrollToRevealSelectionAfterLayout = true; | |
180 else | |
181 scrollToRevealSelection(); | |
182 } | |
183 | |
184 if (AXObjectCache* cache = document().existingAXObjectCache()) | |
185 cache->selectedChildrenChanged(this); | |
186 } | |
187 | |
188 void RenderListBox::layout() | |
189 { | |
190 RenderBlockFlow::layout(); | |
191 | |
192 if (m_vBar) { | |
193 bool enabled = numVisibleItems() < numItems(); | |
194 m_vBar->setEnabled(enabled); | |
195 m_vBar->setProportion(numVisibleItems(), numItems()); | |
196 if (!enabled) { | |
197 scrollToOffsetWithoutAnimation(VerticalScrollbar, 0); | |
198 m_indexOffset = 0; | |
199 } | |
200 } | |
201 | |
202 if (m_scrollToRevealSelectionAfterLayout) { | |
203 ForceHorriblySlowRectMapping slowRectMapping(*this); | |
204 scrollToRevealSelection(); | |
205 } | |
206 } | |
207 | |
208 void RenderListBox::invalidateTreeAfterLayout(const RenderLayerModelObject& inva lidationContainer) | |
209 { | |
210 repaintScrollbarIfNeeded(); | |
211 RenderBox::invalidateTreeAfterLayout(invalidationContainer); | |
212 } | |
213 | |
214 void RenderListBox::scrollToRevealSelection() | |
215 { | |
216 HTMLSelectElement* select = selectElement(); | |
217 | |
218 m_scrollToRevealSelectionAfterLayout = false; | |
219 | |
220 int firstIndex = listIndexToRenderListBoxIndex(select->activeSelectionStartL istIndex()); | |
221 int lastIndex = listIndexToRenderListBoxIndex(select->activeSelectionEndList Index()); | |
222 if (firstIndex >= 0 && !listIndexIsVisible(lastIndex)) | |
223 scrollToRevealElementAtListIndexInternal(firstIndex); | |
224 } | |
225 | |
226 void RenderListBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, L ayoutUnit& maxLogicalWidth) const | |
227 { | |
228 maxLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal + verticalSc rollbarWidth(); | |
229 if (!style()->width().isPercent()) | |
230 minLogicalWidth = maxLogicalWidth; | |
231 } | |
232 | |
233 void RenderListBox::computePreferredLogicalWidths() | |
234 { | |
235 ASSERT(!m_optionsChanged); | |
236 | |
237 m_minPreferredLogicalWidth = 0; | |
238 m_maxPreferredLogicalWidth = 0; | |
239 RenderStyle* styleToUse = style(); | |
240 | |
241 if (styleToUse->width().isFixed() && styleToUse->width().value() > 0) | |
242 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentB oxLogicalWidthForBoxSizing(styleToUse->width().value()); | |
243 else | |
244 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferred LogicalWidth); | |
245 | |
246 if (styleToUse->minWidth().isFixed() && styleToUse->minWidth().value() > 0) { | |
247 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjust ContentBoxLogicalWidthForBoxSizing(styleToUse->minWidth().value())); | |
248 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjust ContentBoxLogicalWidthForBoxSizing(styleToUse->minWidth().value())); | |
249 } | |
250 | |
251 if (styleToUse->maxWidth().isFixed()) { | |
252 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjust ContentBoxLogicalWidthForBoxSizing(styleToUse->maxWidth().value())); | |
253 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjust ContentBoxLogicalWidthForBoxSizing(styleToUse->maxWidth().value())); | |
254 } | |
255 | |
256 LayoutUnit toAdd = borderAndPaddingWidth(); | |
257 m_minPreferredLogicalWidth += toAdd; | |
258 m_maxPreferredLogicalWidth += toAdd; | |
259 | |
260 clearPreferredLogicalWidthsDirty(); | |
261 } | |
262 | |
263 int RenderListBox::size() const | 93 int RenderListBox::size() const |
264 { | 94 { |
265 int specifiedSize = selectElement()->size(); | 95 int specifiedSize = selectElement()->size(); |
266 if (specifiedSize > 1) | 96 if (specifiedSize > 1) |
267 return std::max(minSize, specifiedSize); | 97 return max(minSize, specifiedSize); |
268 | 98 |
269 return defaultSize; | 99 return defaultSize; |
270 } | 100 } |
271 | 101 |
272 int RenderListBox::numVisibleItems() const | 102 LayoutUnit RenderListBox::defaultItemHeight() const |
273 { | 103 { |
274 // Only count fully visible rows. But don't return 0 even if only part of a row shows. | 104 return style()->fontMetrics().height(); |
275 return std::max<int>(1, (contentHeight() + rowSpacing) / itemHeight()); | |
276 } | 105 } |
277 | 106 |
278 int RenderListBox::numItems() const | 107 LayoutUnit RenderListBox::itemHeight() const |
keishi
2014/06/22 11:59:56
Firefox takes the height of the first list item an
| |
279 { | 108 { |
280 return m_listItemCount; | 109 HTMLSelectElement* select = selectElement(); |
281 } | 110 if (!select) |
282 | 111 return 0; |
283 LayoutUnit RenderListBox::listHeight() const | 112 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& items = selectElem ent()->listItems(); |
284 { | 113 if (items.size() == 0) |
285 return itemHeight() * numItems() - rowSpacing; | 114 return defaultItemHeight(); |
115 const Member<HTMLElement>& firstItem = items.first(); | |
116 if (isHTMLOptionElement(firstItem)) { | |
117 RenderObject* optionRenderer = firstItem->renderer(); | |
118 if (!optionRenderer) | |
119 return defaultItemHeight(); | |
120 return optionRenderer->absoluteBoundingBoxRect().height(); | |
121 } | |
122 if (isHTMLOptGroupElement(firstItem)) | |
123 return toHTMLOptGroupElement(firstItem)->groupLabelHeight(); | |
124 return defaultItemHeight(); | |
286 } | 125 } |
287 | 126 |
288 void RenderListBox::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, Logi calExtentComputedValues& computedValues) const | 127 void RenderListBox::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, Logi calExtentComputedValues& computedValues) const |
289 { | 128 { |
290 LayoutUnit height = itemHeight() * size() - rowSpacing; | 129 LayoutUnit height = itemHeight() * size(); |
291 // FIXME: The item height should have been added before updateLogicalHeight was called to avoid this hack. | 130 // FIXME: The item height should have been added before updateLogicalHeight was called to avoid this hack. |
292 updateIntrinsicContentLogicalHeight(height); | 131 updateIntrinsicContentLogicalHeight(height); |
293 | 132 |
294 height += borderAndPaddingHeight(); | 133 height += borderAndPaddingHeight(); |
295 | 134 |
296 RenderBox::computeLogicalHeight(height, logicalTop, computedValues); | 135 RenderBox::computeLogicalHeight(height, logicalTop, computedValues); |
297 } | 136 } |
298 | 137 |
299 int RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, L ineDirectionMode lineDirection, LinePositionMode linePositionMode) const | 138 int RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, L ineDirectionMode lineDirection, LinePositionMode linePositionMode) const |
300 { | 139 { |
301 return RenderBox::baselinePosition(baselineType, firstLine, lineDirection, l inePositionMode) - baselineAdjustment; | 140 return RenderBox::baselinePosition(baselineType, firstLine, lineDirection, l inePositionMode) - baselineAdjustment; |
302 } | 141 } |
303 | 142 |
304 LayoutRect RenderListBox::itemBoundingBoxRectInternal(const LayoutPoint& additio nalOffset, int index) const | |
305 { | |
306 // For RTL, items start after the left-side vertical scrollbar. | |
307 int scrollbarOffset = style()->shouldPlaceBlockDirectionScrollbarOnLogicalLe ft() ? verticalScrollbarWidth() : 0; | |
308 return LayoutRect(additionalOffset.x() + borderLeft() + paddingLeft() + scro llbarOffset, | |
309 additionalOffset.y() + borderTop() + paddingTop() + itemHeight() * (inde x - m_indexOffset), | |
310 contentWidth(), itemHeight()); | |
311 } | |
312 | |
313 void RenderListBox::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOf fset) | |
314 { | |
315 if (style()->visibility() != VISIBLE) | |
316 return; | |
317 | |
318 int listItemsSize = numItems(); | |
319 | |
320 if (paintInfo.phase == PaintPhaseForeground) { | |
321 int index = m_indexOffset; | |
322 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems ()) { | |
323 paintItemForeground(paintInfo, paintOffset, index); | |
324 index++; | |
325 } | |
326 } | |
327 | |
328 // Paint the children. | |
329 RenderBlockFlow::paintObject(paintInfo, paintOffset); | |
330 | |
331 switch (paintInfo.phase) { | |
332 // Depending on whether we have overlay scrollbars they | |
333 // get rendered in the foreground or background phases | |
334 case PaintPhaseForeground: | |
335 if (m_vBar->isOverlayScrollbar()) | |
336 paintScrollbar(paintInfo, paintOffset); | |
337 break; | |
338 case PaintPhaseBlockBackground: | |
339 if (!m_vBar->isOverlayScrollbar()) | |
340 paintScrollbar(paintInfo, paintOffset); | |
341 break; | |
342 case PaintPhaseChildBlockBackground: | |
343 case PaintPhaseChildBlockBackgrounds: { | |
344 int index = m_indexOffset; | |
345 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems ()) { | |
346 paintItemBackground(paintInfo, paintOffset, index); | |
347 index++; | |
348 } | |
349 break; | |
350 } | |
351 default: | |
352 break; | |
353 } | |
354 } | |
355 | |
356 void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) | |
357 { | |
358 if (!isSpatialNavigationEnabled(frame())) | |
359 return RenderBlockFlow::addFocusRingRects(rects, additionalOffset, paint Container); | |
360 | |
361 HTMLSelectElement* select = selectElement(); | |
362 | |
363 // Focus the last selected item. | |
364 int selectedItem = select->activeSelectionEndListIndex(); | |
365 if (selectedItem >= 0) { | |
366 rects.append(pixelSnappedIntRect(itemBoundingBoxRectInternal(additionalO ffset, selectedItem))); | |
367 return; | |
368 } | |
369 | |
370 // No selected items, find the first non-disabled item. | |
371 int size = numItems(); | |
372 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select ->listItems(); | |
373 for (int i = 0; i < size; ++i) { | |
374 HTMLElement* element = listItems[renderListBoxIndexToListIndex(i)]; | |
375 if (isHTMLOptionElement(*element) && !element->isDisabledFormControl()) { | |
376 rects.append(pixelSnappedIntRect(itemBoundingBoxRectInternal(additio nalOffset, i))); | |
377 return; | |
378 } | |
379 } | |
380 } | |
381 | |
382 int RenderListBox::scrollbarLeft() const | |
383 { | |
384 int scrollbarLeft = 0; | |
385 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) | |
386 scrollbarLeft = borderLeft(); | |
387 else | |
388 scrollbarLeft = width() - borderRight() - (m_vBar ? m_vBar->width() : 0) ; | |
389 return scrollbarLeft; | |
390 } | |
391 | |
392 void RenderListBox::paintScrollbar(PaintInfo& paintInfo, const LayoutPoint& pain tOffset) | |
393 { | |
394 if (m_vBar) { | |
395 IntRect scrollRect = pixelSnappedIntRect(paintOffset.x() + scrollbarLeft (), | |
396 paintOffset.y() + borderTop(), | |
397 m_vBar->width(), | |
398 height() - (borderTop() + borderBottom())); | |
399 m_vBar->setFrameRect(scrollRect); | |
400 m_vBar->paint(paintInfo.context, paintInfo.rect); | |
401 } | |
402 } | |
403 | |
404 static LayoutSize itemOffsetForAlignment(TextRun textRun, RenderStyle* itemStyle , Font itemFont, LayoutRect itemBoudingBox) | |
405 { | |
406 ETextAlign actualAlignment = itemStyle->textAlign(); | |
407 // FIXME: Firefox doesn't respect JUSTIFY. Should we? | |
408 // FIXME: Handle TAEND here | |
409 if (actualAlignment == TASTART || actualAlignment == JUSTIFY) | |
410 actualAlignment = itemStyle->isLeftToRightDirection() ? LEFT : RIGHT; | |
411 | |
412 LayoutSize offset = LayoutSize(0, itemFont.fontMetrics().ascent()); | |
413 if (actualAlignment == RIGHT || actualAlignment == WEBKIT_RIGHT) { | |
414 float textWidth = itemFont.width(textRun); | |
415 offset.setWidth(itemBoudingBox.width() - textWidth - optionsSpacingHoriz ontal); | |
416 } else if (actualAlignment == CENTER || actualAlignment == WEBKIT_CENTER) { | |
417 float textWidth = itemFont.width(textRun); | |
418 offset.setWidth((itemBoudingBox.width() - textWidth) / 2); | |
419 } else | |
420 offset.setWidth(optionsSpacingHorizontal); | |
421 return offset; | |
422 } | |
423 | |
424 void RenderListBox::paintItemForeground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex) | |
425 { | |
426 FontCachePurgePreventer fontCachePurgePreventer; | |
427 | |
428 HTMLSelectElement* select = selectElement(); | |
429 | |
430 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select ->listItems(); | |
431 HTMLElement* element = listItems[renderListBoxIndexToListIndex(listIndex)]; | |
432 | |
433 RenderStyle* itemStyle = element->renderStyle(); | |
434 if (!itemStyle) | |
435 itemStyle = style(); | |
436 | |
437 if (itemStyle->visibility() == HIDDEN) | |
438 return; | |
439 | |
440 String itemText; | |
441 bool isOptionElement = isHTMLOptionElement(*element); | |
442 if (isOptionElement) | |
443 itemText = toHTMLOptionElement(*element).textIndentedToRespectGroupLabel (); | |
444 else if (isHTMLOptGroupElement(*element)) | |
445 itemText = toHTMLOptGroupElement(*element).groupLabelText(); | |
446 applyTextTransform(style(), itemText, ' '); | |
447 | |
448 Color textColor = element->renderStyle() ? resolveColor(element->renderStyle (), CSSPropertyColor) : resolveColor(CSSPropertyColor); | |
449 if (isOptionElement && ((toHTMLOptionElement(*element).selected() && select- >suggestedIndex() < 0) || listIndex == select->suggestedIndex())) { | |
450 if (frame()->selection().isFocusedAndActive() && document().focusedEleme nt() == node()) | |
451 textColor = RenderTheme::theme().activeListBoxSelectionForegroundCol or(); | |
452 // Honor the foreground color for disabled items | |
453 else if (!element->isDisabledFormControl() && !select->isDisabledFormCon trol()) | |
454 textColor = RenderTheme::theme().inactiveListBoxSelectionForegroundC olor(); | |
455 } | |
456 | |
457 paintInfo.context->setFillColor(textColor); | |
458 | |
459 TextRun textRun(itemText, 0, 0, TextRun::AllowTrailingExpansion, itemStyle-> direction(), isOverride(itemStyle->unicodeBidi()), true, TextRun::NoRounding); | |
460 Font itemFont = style()->font(); | |
461 LayoutRect r = itemBoundingBoxRectInternal(paintOffset, listIndex); | |
462 r.move(itemOffsetForAlignment(textRun, itemStyle, itemFont, r)); | |
463 | |
464 if (isHTMLOptGroupElement(*element)) { | |
465 FontDescription d = itemFont.fontDescription(); | |
466 d.setWeight(d.bolderWeight()); | |
467 itemFont = Font(d); | |
468 itemFont.update(document().styleEngine()->fontSelector()); | |
469 } | |
470 | |
471 // Draw the item text | |
472 TextRunPaintInfo textRunPaintInfo(textRun); | |
473 textRunPaintInfo.bounds = r; | |
474 paintInfo.context->drawBidiText(itemFont, textRunPaintInfo, roundedIntPoint( r.location())); | |
475 } | |
476 | |
477 void RenderListBox::paintItemBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex) | |
478 { | |
479 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select Element()->listItems(); | |
480 HTMLElement* element = listItems[renderListBoxIndexToListIndex(listIndex)]; | |
481 | |
482 Color backColor; | |
483 if (isHTMLOptionElement(*element) && ((toHTMLOptionElement(*element).selecte d() && selectElement()->suggestedIndex() < 0) || listIndex == selectElement()->s uggestedIndex())) { | |
484 if (frame()->selection().isFocusedAndActive() && document().focusedEleme nt() == node()) | |
485 backColor = RenderTheme::theme().activeListBoxSelectionBackgroundCol or(); | |
486 else | |
487 backColor = RenderTheme::theme().inactiveListBoxSelectionBackgroundC olor(); | |
488 } else { | |
489 backColor = element->renderStyle() ? resolveColor(element->renderStyle() , CSSPropertyBackgroundColor) : resolveColor(CSSPropertyBackgroundColor); | |
490 } | |
491 | |
492 // Draw the background for this list box item | |
493 if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDE N) { | |
494 LayoutRect itemRect = itemBoundingBoxRectInternal(paintOffset, listIndex ); | |
495 itemRect.intersect(controlClipRect(paintOffset)); | |
496 paintInfo.context->fillRect(pixelSnappedIntRect(itemRect), backColor); | |
497 } | |
498 } | |
499 | |
500 bool RenderListBox::isPointInOverflowControl(HitTestResult& result, const Layout Point& locationInContainer, const LayoutPoint& accumulatedOffset) | |
501 { | |
502 if (!m_vBar || !m_vBar->shouldParticipateInHitTesting()) | |
503 return false; | |
504 | |
505 LayoutRect vertRect(accumulatedOffset.x() + scrollbarLeft(), | |
506 accumulatedOffset.y() + borderTop(), | |
507 verticalScrollbarWidth(), | |
508 height() - borderTop() - borderBottom()); | |
509 | |
510 if (vertRect.contains(locationInContainer)) { | |
511 result.setScrollbar(m_vBar.get()); | |
512 return true; | |
513 } | |
514 return false; | |
515 } | |
516 | |
517 int RenderListBox::listIndexAtOffset(const LayoutSize& offset) const | |
518 { | |
519 if (!numItems()) | |
520 return -1; | |
521 | |
522 if (offset.height() < borderTop() + paddingTop() || offset.height() > height () - paddingBottom() - borderBottom()) | |
523 return -1; | |
524 | |
525 int scrollbarWidth = verticalScrollbarWidth(); | |
526 int rightScrollbarOffset = style()->shouldPlaceBlockDirectionScrollbarOnLogi calLeft() ? scrollbarWidth : 0; | |
527 int leftScrollbarOffset = style()->shouldPlaceBlockDirectionScrollbarOnLogic alLeft() ? 0 : scrollbarWidth; | |
528 if (offset.width() < borderLeft() + paddingLeft() + rightScrollbarOffset | |
529 || offset.width() > width() - borderRight() - paddingRight() - leftScrol lbarOffset) | |
530 return -1; | |
531 | |
532 int newOffset = (offset.height() - borderTop() - paddingTop()) / itemHeight( ) + m_indexOffset; | |
533 return newOffset < numItems() ? renderListBoxIndexToListIndex(newOffset) : - 1; | |
534 } | |
535 | |
536 void RenderListBox::panScroll(const IntPoint& panStartMousePosition) | |
537 { | |
538 const int maxSpeed = 20; | |
539 const int iconRadius = 7; | |
540 const int speedReducer = 4; | |
541 | |
542 // FIXME: This doesn't work correctly with transforms. | |
543 FloatPoint absOffset = localToAbsolute(); | |
544 | |
545 IntPoint lastKnownMousePosition = frame()->eventHandler().lastKnownMousePosi tion(); | |
546 // We need to check if the last known mouse position is out of the window. W hen the mouse is out of the window, the position is incoherent | |
547 static IntPoint previousMousePosition; | |
548 if (lastKnownMousePosition.y() < 0) | |
549 lastKnownMousePosition = previousMousePosition; | |
550 else | |
551 previousMousePosition = lastKnownMousePosition; | |
552 | |
553 int yDelta = lastKnownMousePosition.y() - panStartMousePosition.y(); | |
554 | |
555 // If the point is too far from the center we limit the speed | |
556 yDelta = std::max<int>(std::min<int>(yDelta, maxSpeed), -maxSpeed); | |
557 | |
558 if (abs(yDelta) < iconRadius) // at the center we let the space for the icon | |
559 return; | |
560 | |
561 if (yDelta > 0) | |
562 absOffset.move(0, listHeight().toFloat()); | |
563 else if (yDelta < 0) | |
564 yDelta--; | |
565 | |
566 // Let's attenuate the speed | |
567 yDelta /= speedReducer; | |
568 | |
569 IntPoint scrollPoint(0, 0); | |
570 scrollPoint.setY(absOffset.y() + yDelta); | |
571 int newOffset = scrollToward(scrollPoint); | |
572 if (newOffset < 0) | |
573 return; | |
574 | |
575 m_inAutoscroll = true; | |
576 HTMLSelectElement* select = selectElement(); | |
577 select->updateListBoxSelection(!select->multiple()); | |
578 m_inAutoscroll = false; | |
579 } | |
580 | |
581 int RenderListBox::scrollToward(const IntPoint& destination) | |
582 { | |
583 // FIXME: This doesn't work correctly with transforms. | |
584 FloatPoint absPos = localToAbsolute(); | |
585 IntSize positionOffset = roundedIntSize(destination - absPos); | |
586 | |
587 int rows = numVisibleItems(); | |
588 int offset = m_indexOffset; | |
589 | |
590 if (positionOffset.height() < borderTop() + paddingTop() && scrollToRevealEl ementAtListIndexInternal(offset - 1)) | |
591 return offset - 1; | |
592 | |
593 if (positionOffset.height() > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndexInternal(offset + rows)) | |
594 return offset + rows - 1; | |
595 | |
596 return listIndexAtOffset(positionOffset); | |
597 } | |
598 | |
599 void RenderListBox::autoscroll(const IntPoint&) | |
600 { | |
601 IntPoint pos = frame()->view()->windowToContents(frame()->eventHandler().las tKnownMousePosition()); | |
602 | |
603 int endIndex = scrollToward(pos); | |
604 if (selectElement()->isDisabledFormControl()) | |
605 return; | |
606 | |
607 if (endIndex >= 0) { | |
608 HTMLSelectElement* select = selectElement(); | |
609 m_inAutoscroll = true; | |
610 | |
611 if (!select->multiple()) | |
612 select->setActiveSelectionAnchorIndex(renderListBoxIndexToListIndex( endIndex)); | |
613 | |
614 select->setActiveSelectionEndIndex(renderListBoxIndexToListIndex(endInde x)); | |
615 select->updateListBoxSelection(!select->multiple()); | |
616 m_inAutoscroll = false; | |
617 } | |
618 } | |
619 | |
620 void RenderListBox::stopAutoscroll() | |
621 { | |
622 if (selectElement()->isDisabledFormControl()) | |
623 return; | |
624 | |
625 selectElement()->listBoxOnChange(); | |
626 } | |
627 | |
628 bool RenderListBox::scrollToRevealElementAtListIndexInternal(int index) | |
629 { | |
630 if (index < 0 || index >= numItems() || listIndexIsVisible(index)) | |
631 return false; | |
632 | |
633 int newOffset; | |
634 if (index < m_indexOffset) | |
635 newOffset = index; | |
636 else | |
637 newOffset = index - numVisibleItems() + 1; | |
638 | |
639 scrollToOffsetWithoutAnimation(VerticalScrollbar, newOffset); | |
640 | |
641 return true; | |
642 } | |
643 | |
644 bool RenderListBox::listIndexIsVisible(int index) const | |
645 { | |
646 return index >= m_indexOffset && index < m_indexOffset + numVisibleItems(); | |
647 } | |
648 | |
649 bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granular ity, float multiplier) | |
650 { | |
651 return ScrollableArea::scroll(direction, granularity, multiplier); | |
652 } | |
653 | |
654 int RenderListBox::scrollSize(ScrollbarOrientation orientation) const | |
655 { | |
656 return orientation == VerticalScrollbar ? (numItems() - numVisibleItems()) : 0; | |
657 } | |
658 | |
659 IntPoint RenderListBox::scrollPosition() const | |
660 { | |
661 return IntPoint(0, m_indexOffset); | |
662 } | |
663 | |
664 void RenderListBox::setScrollOffset(const IntPoint& offset) | |
665 { | |
666 scrollTo(offset.y()); | |
667 } | |
668 | |
669 void RenderListBox::scrollTo(int newOffset) | |
670 { | |
671 if (newOffset == m_indexOffset) | |
672 return; | |
673 | |
674 m_indexOffset = newOffset; | |
675 | |
676 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && frameView()->isIn PerformLayout()) | |
677 setShouldDoFullPaintInvalidationAfterLayout(true); | |
678 else | |
679 paintInvalidationForWholeRenderer(); | |
680 | |
681 node()->document().enqueueScrollEventForNode(node()); | |
682 } | |
683 | |
684 LayoutUnit RenderListBox::itemHeight() const | |
685 { | |
686 return style()->fontMetrics().height() + rowSpacing; | |
687 } | |
688 | |
689 int RenderListBox::verticalScrollbarWidth() const | |
690 { | |
691 return m_vBar && !m_vBar->isOverlayScrollbar() ? m_vBar->width() : 0; | |
692 } | |
693 | |
694 // FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's | |
695 // how the control currently paints. | |
696 LayoutUnit RenderListBox::scrollWidth() const | |
697 { | |
698 // There is no horizontal scrolling allowed. | |
699 return clientWidth(); | |
700 } | |
701 | |
702 LayoutUnit RenderListBox::scrollHeight() const | |
703 { | |
704 return std::max(clientHeight(), listHeight()); | |
705 } | |
706 | |
707 LayoutUnit RenderListBox::scrollLeft() const | |
708 { | |
709 return 0; | |
710 } | |
711 | |
712 void RenderListBox::setScrollLeft(LayoutUnit) | |
713 { | |
714 } | |
715 | |
716 LayoutUnit RenderListBox::scrollTop() const | |
717 { | |
718 return m_indexOffset * itemHeight(); | |
719 } | |
720 | |
721 void RenderListBox::setScrollTop(LayoutUnit newTop) | |
722 { | |
723 // Determine an index and scroll to it. | |
724 int index = newTop / itemHeight(); | |
725 if (index < 0 || index >= numItems() || index == m_indexOffset) | |
726 return; | |
727 | |
728 scrollToOffsetWithoutAnimation(VerticalScrollbar, index); | |
729 } | |
730 | |
731 bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re sult, const HitTestLocation& locationInContainer, const LayoutPoint& accumulated Offset, HitTestAction hitTestAction) | |
732 { | |
733 if (!RenderBlockFlow::nodeAtPoint(request, result, locationInContainer, accu mulatedOffset, hitTestAction)) | |
734 return false; | |
735 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select Element()->listItems(); | |
736 int size = numItems(); | |
737 LayoutPoint adjustedLocation = accumulatedOffset + location(); | |
738 | |
739 for (int i = 0; i < size; ++i) { | |
740 if (itemBoundingBoxRectInternal(adjustedLocation, i).contains(locationIn Container.point())) { | |
741 if (Element* node = listItems[renderListBoxIndexToListIndex(i)]) { | |
742 result.setInnerNode(node); | |
743 if (!result.innerNonSharedNode()) | |
744 result.setInnerNonSharedNode(node); | |
745 result.setLocalPoint(locationInContainer.point() - toLayoutSize( adjustedLocation)); | |
746 break; | |
747 } | |
748 } | |
749 } | |
750 | |
751 return true; | |
752 } | |
753 | |
754 LayoutRect RenderListBox::controlClipRect(const LayoutPoint& additionalOffset) c onst | |
755 { | |
756 LayoutRect clipRect = contentBoxRect(); | |
757 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) | |
758 clipRect.moveBy(additionalOffset + LayoutPoint(verticalScrollbarWidth(), 0)); | |
759 else | |
760 clipRect.moveBy(additionalOffset); | |
761 return clipRect; | |
762 } | |
763 | |
764 bool RenderListBox::isActive() const | |
765 { | |
766 Page* page = frame()->page(); | |
767 return page && page->focusController().isActive(); | |
768 } | |
769 | |
770 void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) | |
771 { | |
772 IntRect scrollRect = rect; | |
773 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) | |
774 scrollRect.move(borderLeft(), borderTop()); | |
775 else | |
776 scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop( )); | |
777 | |
778 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && frameView()->isIn PerformLayout()) { | |
779 m_verticalBarDamage = scrollRect; | |
780 m_hasVerticalBarDamage = true; | |
781 } else { | |
782 invalidatePaintRectangle(scrollRect); | |
783 } | |
784 } | |
785 | |
786 void RenderListBox::repaintScrollbarIfNeeded() | |
787 { | |
788 if (!hasVerticalBarDamage()) | |
789 return; | |
790 invalidatePaintRectangle(verticalBarDamage()); | |
791 | |
792 resetScrollbarDamage(); | |
793 } | |
794 | |
795 IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scr ollbar, const IntRect& scrollbarRect) const | |
796 { | |
797 RenderView* view = this->view(); | |
798 if (!view) | |
799 return scrollbarRect; | |
800 | |
801 IntRect rect = scrollbarRect; | |
802 | |
803 int scrollbarTop = borderTop(); | |
804 rect.move(scrollbarLeft(), scrollbarTop); | |
805 | |
806 return view->frameView()->convertFromRenderer(*this, rect); | |
807 } | |
808 | |
809 IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scr ollbar, const IntRect& parentRect) const | |
810 { | |
811 RenderView* view = this->view(); | |
812 if (!view) | |
813 return parentRect; | |
814 | |
815 IntRect rect = view->frameView()->convertToRenderer(*this, parentRect); | |
816 | |
817 int scrollbarTop = borderTop(); | |
818 rect.move(-scrollbarLeft(), -scrollbarTop); | |
819 return rect; | |
820 } | |
821 | |
822 IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* sc rollbar, const IntPoint& scrollbarPoint) const | |
823 { | |
824 RenderView* view = this->view(); | |
825 if (!view) | |
826 return scrollbarPoint; | |
827 | |
828 IntPoint point = scrollbarPoint; | |
829 | |
830 int scrollbarTop = borderTop(); | |
831 point.move(scrollbarLeft(), scrollbarTop); | |
832 | |
833 return view->frameView()->convertFromRenderer(*this, point); | |
834 } | |
835 | |
836 IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* sc rollbar, const IntPoint& parentPoint) const | |
837 { | |
838 RenderView* view = this->view(); | |
839 if (!view) | |
840 return parentPoint; | |
841 | |
842 IntPoint point = view->frameView()->convertToRenderer(*this, parentPoint); | |
843 | |
844 int scrollbarTop = borderTop(); | |
845 point.move(-scrollbarLeft(), -scrollbarTop); | |
846 return point; | |
847 } | |
848 | |
849 IntSize RenderListBox::contentsSize() const | |
850 { | |
851 return IntSize(scrollWidth(), scrollHeight()); | |
852 } | |
853 | |
854 int RenderListBox::visibleHeight() const | |
855 { | |
856 return height(); | |
857 } | |
858 | |
859 int RenderListBox::visibleWidth() const | |
860 { | |
861 return width(); | |
862 } | |
863 | |
864 IntPoint RenderListBox::lastKnownMousePosition() const | |
865 { | |
866 RenderView* view = this->view(); | |
867 if (!view) | |
868 return IntPoint(); | |
869 return view->frameView()->lastKnownMousePosition(); | |
870 } | |
871 | |
872 bool RenderListBox::shouldSuspendScrollAnimations() const | |
873 { | |
874 RenderView* view = this->view(); | |
875 if (!view) | |
876 return true; | |
877 return view->frameView()->shouldSuspendScrollAnimations(); | |
878 } | |
879 | |
880 bool RenderListBox::scrollbarsCanBeActive() const | |
881 { | |
882 RenderView* view = this->view(); | |
883 if (!view) | |
884 return false; | |
885 return view->frameView()->scrollbarsCanBeActive(); | |
886 } | |
887 | |
888 IntPoint RenderListBox::minimumScrollPosition() const | |
889 { | |
890 return IntPoint(); | |
891 } | |
892 | |
893 IntPoint RenderListBox::maximumScrollPosition() const | |
894 { | |
895 return IntPoint(0, std::max(numItems() - numVisibleItems(), 0)); | |
896 } | |
897 | |
898 bool RenderListBox::userInputScrollable(ScrollbarOrientation orientation) const | |
899 { | |
900 return orientation == VerticalScrollbar; | |
901 } | |
902 | |
903 bool RenderListBox::shouldPlaceVerticalScrollbarOnLeft() const | |
904 { | |
905 return false; | |
906 } | |
907 | |
908 int RenderListBox::lineStep(ScrollbarOrientation) const | |
909 { | |
910 return 1; | |
911 } | |
912 | |
913 int RenderListBox::pageStep(ScrollbarOrientation orientation) const | |
914 { | |
915 return std::max(1, numVisibleItems() - 1); | |
916 } | |
917 | |
918 float RenderListBox::pixelStep(ScrollbarOrientation) const | |
919 { | |
920 return 1.0f / itemHeight(); | |
921 } | |
922 | |
923 IntRect RenderListBox::scrollableAreaBoundingBox() const | |
924 { | |
925 return absoluteBoundingBoxRect(); | |
926 } | |
927 | |
928 PassRefPtr<Scrollbar> RenderListBox::createScrollbar() | |
929 { | |
930 RefPtr<Scrollbar> widget; | |
931 bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR); | |
932 if (hasCustomScrollbarStyle) | |
933 widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this->node()); | |
934 else { | |
935 widget = Scrollbar::create(this, VerticalScrollbar, RenderTheme::theme() .scrollbarControlSizeForPart(ListboxPart)); | |
936 didAddScrollbar(widget.get(), VerticalScrollbar); | |
937 } | |
938 document().view()->addChild(widget.get()); | |
939 return widget.release(); | |
940 } | |
941 | |
942 void RenderListBox::destroyScrollbar() | |
943 { | |
944 if (!m_vBar) | |
945 return; | |
946 | |
947 if (!m_vBar->isCustomScrollbar()) | |
948 ScrollableArea::willRemoveScrollbar(m_vBar.get(), VerticalScrollbar); | |
949 m_vBar->removeFromParent(); | |
950 m_vBar->disconnectFromScrollableArea(); | |
951 m_vBar = nullptr; | |
952 } | |
953 | |
954 void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar) | |
955 { | |
956 if (hasScrollbar == (m_vBar != 0)) | |
957 return; | |
958 | |
959 if (hasScrollbar) | |
960 m_vBar = createScrollbar(); | |
961 else | |
962 destroyScrollbar(); | |
963 | |
964 if (m_vBar) | |
965 m_vBar->styleChanged(); | |
966 | |
967 // Force an update since we know the scrollbars have changed things. | |
968 if (document().hasAnnotatedRegions()) | |
969 document().setAnnotatedRegionsDirty(true); | |
970 } | |
971 | |
972 int RenderListBox::renderListBoxIndexToListIndex(int index) const | |
973 { | |
974 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select Element()->listItems(); | |
975 const int size = static_cast<int>(listItems.size()); | |
976 | |
977 if (size == numItems()) | |
978 return index; | |
979 | |
980 int listBoxIndex = 0; | |
981 int listIndex = 0; | |
982 for (; listIndex < size; ++listIndex) { | |
983 const HTMLElement& element = *listItems[listIndex]; | |
984 if (isHTMLOptionElement(element) && toHTMLOptionElement(element).isDispl ayNone()) | |
985 continue; | |
986 if (isHTMLOptGroupElement(element) && toHTMLOptGroupElement(element).isD isplayNone()) | |
987 continue; | |
988 if (index == listBoxIndex) | |
989 break; | |
990 ++listBoxIndex; | |
991 } | |
992 return listIndex; | |
993 } | |
994 | |
995 int RenderListBox::listIndexToRenderListBoxIndex(int index) const | |
996 { | |
997 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select Element()->listItems(); | |
998 const int size = static_cast<int>(listItems.size()); | |
999 | |
1000 if (size == numItems()) | |
1001 return index; | |
1002 | |
1003 int listBoxIndex = 0; | |
1004 for (int listIndex = 0; listIndex < size; ++listIndex) { | |
1005 const HTMLElement& element = *listItems[listIndex]; | |
1006 if (isHTMLOptionElement(element) && toHTMLOptionElement(element).isDispl ayNone()) | |
1007 continue; | |
1008 if (isHTMLOptGroupElement(element) && toHTMLOptGroupElement(element).isD isplayNone()) | |
1009 continue; | |
1010 if (index == listIndex) | |
1011 break; | |
1012 ++listBoxIndex; | |
1013 } | |
1014 return listBoxIndex; | |
1015 } | |
1016 | |
1017 LayoutRect RenderListBox::itemBoundingBoxRect(const LayoutPoint& point, int inde x) const | |
1018 { | |
1019 return itemBoundingBoxRectInternal(point, listIndexToRenderListBoxIndex(inde x)); | |
1020 } | |
1021 | |
1022 bool RenderListBox::scrollToRevealElementAtListIndex(int index) | |
1023 { | |
1024 return scrollToRevealElementAtListIndexInternal(listIndexToRenderListBoxInde x(index)); | |
1025 } | |
1026 | |
1027 } // namespace WebCore | 143 } // namespace WebCore |
OLD | NEW |