Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(219)

Side by Side Diff: Source/WebCore/rendering/RenderListBox.cpp

Issue 14096013: Implement select element list box with shadow DOM. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@shadowselect
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 #include <math.h> 61 #include <math.h>
62 62
63 using namespace std; 63 using namespace std;
64 64
65 namespace WebCore { 65 namespace WebCore {
66 66
67 using namespace HTMLNames; 67 using namespace HTMLNames;
68 68
69 const int rowSpacing = 1; 69 const int rowSpacing = 1;
70 70
71 const int optionsSpacingHorizontal = 2;
72
73 // The minSize constant was originally defined to render scrollbars correctly. 71 // The minSize constant was originally defined to render scrollbars correctly.
74 // This might vary for different platforms. 72 // This might vary for different platforms.
75 const int minSize = 4; 73 const int minSize = 4;
76 74
77 // Default size when the multiple attribute is present but size attribute is abs ent. 75 // Default size when the multiple attribute is present but size attribute is abs ent.
78 const int defaultSize = 4; 76 const int defaultSize = 4;
79 77
80 // FIXME: This hardcoded baselineAdjustment is what we used to do for the old
81 // widget, but I'm not sure this is right for the new control.
82 const int baselineAdjustment = 7;
83
84 RenderListBox::RenderListBox(Element* element) 78 RenderListBox::RenderListBox(Element* element)
85 : RenderBlock(element) 79 : RenderBlock(element)
86 , m_optionsChanged(true)
87 , m_scrollToRevealSelectionAfterLayout(false)
88 , m_inAutoscroll(false)
89 , m_optionsWidth(0)
90 , m_indexOffset(0)
91 { 80 {
92 ASSERT(element); 81 ASSERT(element);
93 ASSERT(element->isHTMLElement()); 82 ASSERT(element->isHTMLElement());
94 ASSERT(element->hasTagName(HTMLNames::selectTag)); 83 ASSERT(element->hasTagName(HTMLNames::selectTag));
95
96 if (FrameView* frameView = frame()->view())
97 frameView->addScrollableArea(this);
98 } 84 }
99 85
100 RenderListBox::~RenderListBox() 86 RenderListBox::~RenderListBox()
101 { 87 {
102 setHasVerticalScrollbar(false);
103
104 if (FrameView* frameView = frame()->view())
105 frameView->removeScrollableArea(this);
106 } 88 }
107 89
108 inline HTMLSelectElement* RenderListBox::selectElement() const 90 inline HTMLSelectElement* RenderListBox::selectElement() const
109 { 91 {
110 return toHTMLSelectElement(node()); 92 return toHTMLSelectElement(node());
111 } 93 }
112 94
113 void RenderListBox::updateFromElement()
114 {
115 FontCachePurgePreventer fontCachePurgePreventer;
116
117 if (m_optionsChanged) {
118 const Vector<HTMLElement*>& listItems = selectElement()->listItems();
119 int size = numItems();
120
121 float width = 0;
122 for (int i = 0; i < size; ++i) {
123 HTMLElement* element = listItems[i];
124 String text;
125 Font itemFont = style()->font();
126 if (element->hasTagName(optionTag))
127 text = toHTMLOptionElement(element)->textIndentedToRespectGroupL abel();
128 else if (element->hasTagName(optgroupTag)) {
129 text = static_cast<const HTMLOptGroupElement*>(element)->groupLa belText();
130 FontDescription d = itemFont.fontDescription();
131 d.setWeight(d.bolderWeight());
132 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacin g());
133 itemFont.update(document()->styleResolver()->fontSelector());
134 }
135
136 if (!text.isEmpty()) {
137 applyTextTransform(style(), text, ' ');
138 // FIXME: Why is this always LTR? Can't text direction affect th e width?
139 TextRun textRun = constructTextRun(this, itemFont, text, style() , TextRun::AllowTrailingExpansion);
140 textRun.disableRoundingHacks();
141 float textWidth = itemFont.width(textRun);
142 width = max(width, textWidth);
143 }
144 }
145 m_optionsWidth = static_cast<int>(ceilf(width));
146 m_optionsChanged = false;
147
148 setHasVerticalScrollbar(true);
149
150 setNeedsLayoutAndPrefWidthsRecalc();
151 }
152 }
153
154 bool RenderListBox::canBeReplacedWithInlineRunIn() const
155 {
156 return false;
157 }
158
159 void RenderListBox::selectionChanged()
160 {
161 repaint();
162 if (!m_inAutoscroll) {
163 if (m_optionsChanged || needsLayout())
164 m_scrollToRevealSelectionAfterLayout = true;
165 else
166 scrollToRevealSelection();
167 }
168
169 if (AXObjectCache* cache = document()->existingAXObjectCache())
170 cache->selectedChildrenChanged(this);
171 }
172
173 void RenderListBox::layout()
174 {
175 StackStats::LayoutCheckPoint layoutCheckPoint;
176 RenderBlock::layout();
177
178 if (m_vBar) {
179 bool enabled = numVisibleItems() < numItems();
180 m_vBar->setEnabled(enabled);
181 m_vBar->setSteps(1, max(1, numVisibleItems() - 1), itemHeight());
182 m_vBar->setProportion(numVisibleItems(), numItems());
183 if (!enabled) {
184 scrollToOffsetWithoutAnimation(VerticalScrollbar, 0);
185 m_indexOffset = 0;
186 }
187 }
188
189 if (m_scrollToRevealSelectionAfterLayout) {
190 LayoutStateDisabler layoutStateDisabler(view());
191 scrollToRevealSelection();
192 }
193 }
194
195 void RenderListBox::scrollToRevealSelection()
196 {
197 HTMLSelectElement* select = selectElement();
198
199 m_scrollToRevealSelectionAfterLayout = false;
200
201 int firstIndex = select->activeSelectionStartListIndex();
202 if (firstIndex >= 0 && !listIndexIsVisible(select->activeSelectionEndListInd ex()))
203 scrollToRevealElementAtListIndex(firstIndex);
204 }
205
206 void RenderListBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, L ayoutUnit& maxLogicalWidth) const
207 {
208 maxLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal;
209 if (m_vBar)
210 maxLogicalWidth += m_vBar->width();
211 if (!style()->width().isPercent())
212 minLogicalWidth = maxLogicalWidth;
213 }
214
215 void RenderListBox::computePreferredLogicalWidths()
216 {
217 ASSERT(!m_optionsChanged);
218
219 m_minPreferredLogicalWidth = 0;
220 m_maxPreferredLogicalWidth = 0;
221
222 if (style()->width().isFixed() && style()->width().value() > 0)
223 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentB oxLogicalWidthForBoxSizing(style()->width().value());
224 else
225 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferred LogicalWidth);
226
227 if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
228 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustConte ntBoxLogicalWidthForBoxSizing(style()->minWidth().value()));
229 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustConte ntBoxLogicalWidthForBoxSizing(style()->minWidth().value()));
230 }
231
232 if (style()->maxWidth().isFixed()) {
233 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustConte ntBoxLogicalWidthForBoxSizing(style()->maxWidth().value()));
234 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustConte ntBoxLogicalWidthForBoxSizing(style()->maxWidth().value()));
235 }
236
237 LayoutUnit toAdd = borderAndPaddingWidth();
238 m_minPreferredLogicalWidth += toAdd;
239 m_maxPreferredLogicalWidth += toAdd;
240
241 setPreferredLogicalWidthsDirty(false);
242 }
243
244 int RenderListBox::size() const 95 int RenderListBox::size() const
245 { 96 {
246 int specifiedSize = selectElement()->size(); 97 int specifiedSize = selectElement()->size();
247 if (specifiedSize > 1) 98 if (specifiedSize > 1)
248 return max(minSize, specifiedSize); 99 return max(minSize, specifiedSize);
249 100
250 return defaultSize; 101 return defaultSize;
251 } 102 }
252 103
253 int RenderListBox::numVisibleItems() const 104 LayoutUnit RenderListBox::itemHeight() const
254 { 105 {
255 // Only count fully visible rows. But don't return 0 even if only part of a row shows. 106 return style()->fontMetrics().height() + rowSpacing;
256 return max<int>(1, (contentHeight() + rowSpacing) / itemHeight());
257 }
258
259 int RenderListBox::numItems() const
260 {
261 return selectElement()->listItems().size();
262 }
263
264 LayoutUnit RenderListBox::listHeight() const
265 {
266 return itemHeight() * numItems() - rowSpacing;
267 } 107 }
268 108
269 void RenderListBox::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, Logi calExtentComputedValues& computedValues) const 109 void RenderListBox::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, Logi calExtentComputedValues& computedValues) const
270 { 110 {
271 LayoutUnit height = itemHeight() * size() - rowSpacing + borderAndPaddingHei ght(); 111 LayoutUnit height = itemHeight() * size() - rowSpacing + borderAndPaddingHei ght();
272 RenderBox::computeLogicalHeight(height, logicalTop, computedValues); 112 RenderBox::computeLogicalHeight(height, logicalTop, computedValues);
273 } 113 }
274 114
275 int RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, L ineDirectionMode lineDirection, LinePositionMode linePositionMode) const
276 {
277 return RenderBox::baselinePosition(baselineType, firstLine, lineDirection, l inePositionMode) - baselineAdjustment;
278 }
279
280 LayoutRect RenderListBox::itemBoundingBoxRect(const LayoutPoint& additionalOffse t, int index)
281 {
282 return LayoutRect(additionalOffset.x() + borderLeft() + paddingLeft(),
283 additionalOffset.y() + borderTop() + paddingTop() + itemHeigh t() * (index - m_indexOffset),
284 contentWidth(), itemHeight());
285 }
286
287 void RenderListBox::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOf fset)
288 {
289 if (style()->visibility() != VISIBLE)
290 return;
291
292 int listItemsSize = numItems();
293
294 if (paintInfo.phase == PaintPhaseForeground) {
295 int index = m_indexOffset;
296 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems ()) {
297 paintItemForeground(paintInfo, paintOffset, index);
298 index++;
299 }
300 }
301
302 // Paint the children.
303 RenderBlock::paintObject(paintInfo, paintOffset);
304
305 switch (paintInfo.phase) {
306 // Depending on whether we have overlay scrollbars they
307 // get rendered in the foreground or background phases
308 case PaintPhaseForeground:
309 if (m_vBar->isOverlayScrollbar())
310 paintScrollbar(paintInfo, paintOffset);
311 break;
312 case PaintPhaseBlockBackground:
313 if (!m_vBar->isOverlayScrollbar())
314 paintScrollbar(paintInfo, paintOffset);
315 break;
316 case PaintPhaseChildBlockBackground:
317 case PaintPhaseChildBlockBackgrounds: {
318 int index = m_indexOffset;
319 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems ()) {
320 paintItemBackground(paintInfo, paintOffset, index);
321 index++;
322 }
323 break;
324 }
325 default:
326 break;
327 }
328 }
329
330 void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer)
331 {
332 if (!isSpatialNavigationEnabled(frame()))
333 return RenderBlock::addFocusRingRects(rects, additionalOffset, paintCont ainer);
334
335 HTMLSelectElement* select = selectElement();
336
337 // Focus the last selected item.
338 int selectedItem = select->activeSelectionEndListIndex();
339 if (selectedItem >= 0) {
340 rects.append(pixelSnappedIntRect(itemBoundingBoxRect(additionalOffset, s electedItem)));
341 return;
342 }
343
344 // No selected items, find the first non-disabled item.
345 int size = numItems();
346 const Vector<HTMLElement*>& listItems = select->listItems();
347 for (int i = 0; i < size; ++i) {
348 HTMLElement* element = listItems[i];
349 if (element->hasTagName(optionTag) && !element->isDisabledFormControl()) {
350 rects.append(pixelSnappedIntRect(itemBoundingBoxRect(additionalOffse t, i)));
351 return;
352 }
353 }
354 }
355
356 void RenderListBox::paintScrollbar(PaintInfo& paintInfo, const LayoutPoint& pain tOffset)
357 {
358 if (m_vBar) {
359 IntRect scrollRect = pixelSnappedIntRect(paintOffset.x() + width() - bor derRight() - m_vBar->width(),
360 paintOffset.y() + borderTop(),
361 m_vBar->width(),
362 height() - (borderTop() + borderBottom()));
363 m_vBar->setFrameRect(scrollRect);
364 m_vBar->paint(paintInfo.context, paintInfo.rect);
365 }
366 }
367
368 static LayoutSize itemOffsetForAlignment(TextRun textRun, RenderStyle* itemStyle , Font itemFont, LayoutRect itemBoudingBox)
369 {
370 ETextAlign actualAlignment = itemStyle->textAlign();
371 // FIXME: Firefox doesn't respect JUSTIFY. Should we?
372 // FIXME: Handle TAEND here
373 if (actualAlignment == TASTART || actualAlignment == JUSTIFY)
374 actualAlignment = itemStyle->isLeftToRightDirection() ? LEFT : RIGHT;
375
376 LayoutSize offset = LayoutSize(0, itemFont.fontMetrics().ascent());
377 if (actualAlignment == RIGHT || actualAlignment == WEBKIT_RIGHT) {
378 float textWidth = itemFont.width(textRun);
379 offset.setWidth(itemBoudingBox.width() - textWidth - optionsSpacingHoriz ontal);
380 } else if (actualAlignment == CENTER || actualAlignment == WEBKIT_CENTER) {
381 float textWidth = itemFont.width(textRun);
382 offset.setWidth((itemBoudingBox.width() - textWidth) / 2);
383 } else
384 offset.setWidth(optionsSpacingHorizontal);
385 return offset;
386 }
387
388 void RenderListBox::paintItemForeground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex)
389 {
390 FontCachePurgePreventer fontCachePurgePreventer;
391
392 HTMLSelectElement* select = selectElement();
393
394 const Vector<HTMLElement*>& listItems = select->listItems();
395 HTMLElement* element = listItems[listIndex];
396
397 RenderStyle* itemStyle = element->renderStyle();
398 if (!itemStyle)
399 itemStyle = style();
400
401 if (itemStyle->visibility() == HIDDEN)
402 return;
403
404 String itemText;
405 bool isOptionElement = element->hasTagName(optionTag);
406 if (isOptionElement)
407 itemText = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel ();
408 else if (element->hasTagName(optgroupTag))
409 itemText = static_cast<const HTMLOptGroupElement*>(element)->groupLabelT ext();
410 applyTextTransform(style(), itemText, ' ');
411
412 Color textColor = element->renderStyle() ? element->renderStyle()->visitedDe pendentColor(CSSPropertyColor) : style()->visitedDependentColor(CSSPropertyColor );
413 if (isOptionElement && toHTMLOptionElement(element)->selected()) {
414 if (frame()->selection()->isFocusedAndActive() && document()->focusedNod e() == node())
415 textColor = theme()->activeListBoxSelectionForegroundColor();
416 // Honor the foreground color for disabled items
417 else if (!element->isDisabledFormControl() && !select->isDisabledFormCon trol())
418 textColor = theme()->inactiveListBoxSelectionForegroundColor();
419 }
420
421 ColorSpace colorSpace = itemStyle->colorSpace();
422 paintInfo.context->setFillColor(textColor, colorSpace);
423
424 TextRun textRun(itemText, 0, 0, TextRun::AllowTrailingExpansion, itemStyle-> direction(), isOverride(itemStyle->unicodeBidi()), true, TextRun::NoRounding);
425 Font itemFont = style()->font();
426 LayoutRect r = itemBoundingBoxRect(paintOffset, listIndex);
427 r.move(itemOffsetForAlignment(textRun, itemStyle, itemFont, r));
428
429 if (element->hasTagName(optgroupTag)) {
430 FontDescription d = itemFont.fontDescription();
431 d.setWeight(d.bolderWeight());
432 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
433 itemFont.update(document()->styleResolver()->fontSelector());
434 }
435
436 // Draw the item text
437 paintInfo.context->drawBidiText(itemFont, textRun, roundedIntPoint(r.locatio n()));
438 }
439
440 void RenderListBox::paintItemBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex)
441 {
442 const Vector<HTMLElement*>& listItems = selectElement()->listItems();
443 HTMLElement* element = listItems[listIndex];
444
445 Color backColor;
446 if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected ()) {
447 if (frame()->selection()->isFocusedAndActive() && document()->focusedNod e() == node())
448 backColor = theme()->activeListBoxSelectionBackgroundColor();
449 else
450 backColor = theme()->inactiveListBoxSelectionBackgroundColor();
451 } else
452 backColor = element->renderStyle() ? element->renderStyle()->visitedDepe ndentColor(CSSPropertyBackgroundColor) : style()->visitedDependentColor(CSSPrope rtyBackgroundColor);
453
454 // Draw the background for this list box item
455 if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDE N) {
456 ColorSpace colorSpace = element->renderStyle() ? element->renderStyle()- >colorSpace() : style()->colorSpace();
457 LayoutRect itemRect = itemBoundingBoxRect(paintOffset, listIndex);
458 itemRect.intersect(controlClipRect(paintOffset));
459 paintInfo.context->fillRect(pixelSnappedIntRect(itemRect), backColor, co lorSpace);
460 }
461 }
462
463 bool RenderListBox::isPointInOverflowControl(HitTestResult& result, const Layout Point& locationInContainer, const LayoutPoint& accumulatedOffset)
464 {
465 if (!m_vBar || !m_vBar->shouldParticipateInHitTesting())
466 return false;
467
468 LayoutRect vertRect(accumulatedOffset.x() + width() - borderRight() - m_vBar ->width(),
469 accumulatedOffset.y() + borderTop(),
470 m_vBar->width(),
471 height() - borderTop() - borderBottom());
472
473 if (vertRect.contains(locationInContainer)) {
474 result.setScrollbar(m_vBar.get());
475 return true;
476 }
477 return false;
478 }
479
480 int RenderListBox::listIndexAtOffset(const LayoutSize& offset)
481 {
482 if (!numItems())
483 return -1;
484
485 if (offset.height() < borderTop() + paddingTop() || offset.height() > height () - paddingBottom() - borderBottom())
486 return -1;
487
488 int scrollbarWidth = m_vBar ? m_vBar->width() : 0;
489 if (offset.width() < borderLeft() + paddingLeft() || offset.width() > width( ) - borderRight() - paddingRight() - scrollbarWidth)
490 return -1;
491
492 int newOffset = (offset.height() - borderTop() - paddingTop()) / itemHeight( ) + m_indexOffset;
493 return newOffset < numItems() ? newOffset : -1;
494 }
495
496 void RenderListBox::panScroll(const IntPoint& panStartMousePosition)
497 {
498 const int maxSpeed = 20;
499 const int iconRadius = 7;
500 const int speedReducer = 4;
501
502 // FIXME: This doesn't work correctly with transforms.
503 FloatPoint absOffset = localToAbsolute();
504
505 IntPoint lastKnownMousePosition = frame()->eventHandler()->lastKnownMousePos ition();
506 // 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
507 static IntPoint previousMousePosition;
508 if (lastKnownMousePosition.y() < 0)
509 lastKnownMousePosition = previousMousePosition;
510 else
511 previousMousePosition = lastKnownMousePosition;
512
513 int yDelta = lastKnownMousePosition.y() - panStartMousePosition.y();
514
515 // If the point is too far from the center we limit the speed
516 yDelta = max<int>(min<int>(yDelta, maxSpeed), -maxSpeed);
517
518 if (abs(yDelta) < iconRadius) // at the center we let the space for the icon
519 return;
520
521 if (yDelta > 0)
522 //offsetY = view()->viewHeight();
523 absOffset.move(0, listHeight());
524 else if (yDelta < 0)
525 yDelta--;
526
527 // Let's attenuate the speed
528 yDelta /= speedReducer;
529
530 IntPoint scrollPoint(0, 0);
531 scrollPoint.setY(absOffset.y() + yDelta);
532 int newOffset = scrollToward(scrollPoint);
533 if (newOffset < 0)
534 return;
535
536 m_inAutoscroll = true;
537 HTMLSelectElement* select = selectElement();
538 select->updateListBoxSelection(!select->multiple());
539 m_inAutoscroll = false;
540 }
541
542 int RenderListBox::scrollToward(const IntPoint& destination)
543 {
544 // FIXME: This doesn't work correctly with transforms.
545 FloatPoint absPos = localToAbsolute();
546 IntSize positionOffset = roundedIntSize(destination - absPos);
547
548 int rows = numVisibleItems();
549 int offset = m_indexOffset;
550
551 if (positionOffset.height() < borderTop() + paddingTop() && scrollToRevealEl ementAtListIndex(offset - 1))
552 return offset - 1;
553
554 if (positionOffset.height() > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndex(offset + rows))
555 return offset + rows - 1;
556
557 return listIndexAtOffset(positionOffset);
558 }
559
560 void RenderListBox::autoscroll(const IntPoint&)
561 {
562 IntPoint pos = frame()->view()->windowToContents(frame()->eventHandler()->la stKnownMousePosition());
563
564 int endIndex = scrollToward(pos);
565 if (endIndex >= 0) {
566 HTMLSelectElement* select = selectElement();
567 m_inAutoscroll = true;
568
569 if (!select->multiple())
570 select->setActiveSelectionAnchorIndex(endIndex);
571
572 select->setActiveSelectionEndIndex(endIndex);
573 select->updateListBoxSelection(!select->multiple());
574 m_inAutoscroll = false;
575 }
576 }
577
578 void RenderListBox::stopAutoscroll()
579 {
580 selectElement()->listBoxOnChange();
581 }
582
583 bool RenderListBox::scrollToRevealElementAtListIndex(int index)
584 {
585 if (index < 0 || index >= numItems() || listIndexIsVisible(index))
586 return false;
587
588 int newOffset;
589 if (index < m_indexOffset)
590 newOffset = index;
591 else
592 newOffset = index - numVisibleItems() + 1;
593
594 scrollToOffsetWithoutAnimation(VerticalScrollbar, newOffset);
595
596 return true;
597 }
598
599 bool RenderListBox::listIndexIsVisible(int index)
600 {
601 return index >= m_indexOffset && index < m_indexOffset + numVisibleItems();
602 }
603
604 bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granular ity, float multiplier, Node**)
605 {
606 return ScrollableArea::scroll(direction, granularity, multiplier);
607 }
608
609 bool RenderListBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranul arity granularity, float multiplier, Node**)
610 {
611 return ScrollableArea::scroll(logicalToPhysical(direction, style()->isHorizo ntalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multipli er);
612 }
613
614 void RenderListBox::valueChanged(unsigned listIndex)
615 {
616 HTMLSelectElement* element = selectElement();
617 element->setSelectedIndex(element->listToOptionIndex(listIndex));
618 element->dispatchFormControlChangeEvent();
619 }
620
621 int RenderListBox::scrollSize(ScrollbarOrientation orientation) const
622 {
623 return ((orientation == VerticalScrollbar) && m_vBar) ? (m_vBar->totalSize() - m_vBar->visibleSize()) : 0;
624 }
625
626 int RenderListBox::scrollPosition(Scrollbar*) const
627 {
628 return m_indexOffset;
629 }
630
631 void RenderListBox::setScrollOffset(const IntPoint& offset)
632 {
633 scrollTo(offset.y());
634 }
635
636 void RenderListBox::scrollTo(int newOffset)
637 {
638 if (newOffset == m_indexOffset)
639 return;
640
641 m_indexOffset = newOffset;
642 repaint();
643 node()->document()->eventQueue()->enqueueOrDispatchScrollEvent(node(), Docum entEventQueue::ScrollEventElementTarget);
644 }
645
646 LayoutUnit RenderListBox::itemHeight() const
647 {
648 return style()->fontMetrics().height() + rowSpacing;
649 }
650
651 int RenderListBox::verticalScrollbarWidth() const
652 {
653 return m_vBar && !m_vBar->isOverlayScrollbar() ? m_vBar->width() : 0;
654 }
655
656 // FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's
657 // how the control currently paints.
658 int RenderListBox::scrollWidth() const
659 {
660 // There is no horizontal scrolling allowed.
661 return pixelSnappedClientWidth();
662 }
663
664 int RenderListBox::scrollHeight() const
665 {
666 return max(pixelSnappedClientHeight(), roundToInt(listHeight()));
667 }
668
669 int RenderListBox::scrollLeft() const
670 {
671 return 0;
672 }
673
674 void RenderListBox::setScrollLeft(int)
675 {
676 }
677
678 int RenderListBox::scrollTop() const
679 {
680 return m_indexOffset * itemHeight();
681 }
682
683 void RenderListBox::setScrollTop(int newTop)
684 {
685 // Determine an index and scroll to it.
686 int index = newTop / itemHeight();
687 if (index < 0 || index >= numItems() || index == m_indexOffset)
688 return;
689
690 scrollToOffsetWithoutAnimation(VerticalScrollbar, index);
691 }
692
693 bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re sult, const HitTestLocation& locationInContainer, const LayoutPoint& accumulated Offset, HitTestAction hitTestAction)
694 {
695 if (!RenderBlock::nodeAtPoint(request, result, locationInContainer, accumula tedOffset, hitTestAction))
696 return false;
697 const Vector<HTMLElement*>& listItems = selectElement()->listItems();
698 int size = numItems();
699 LayoutPoint adjustedLocation = accumulatedOffset + location();
700
701 for (int i = 0; i < size; ++i) {
702 if (itemBoundingBoxRect(adjustedLocation, i).contains(locationInContaine r.point())) {
703 if (Element* node = listItems[i]) {
704 result.setInnerNode(node);
705 if (!result.innerNonSharedNode())
706 result.setInnerNonSharedNode(node);
707 result.setLocalPoint(locationInContainer.point() - toLayoutSize( adjustedLocation));
708 break;
709 }
710 }
711 }
712
713 return true;
714 }
715
716 LayoutRect RenderListBox::controlClipRect(const LayoutPoint& additionalOffset) c onst
717 {
718 LayoutRect clipRect = contentBoxRect();
719 clipRect.moveBy(additionalOffset);
720 return clipRect;
721 }
722
723 bool RenderListBox::isActive() const
724 {
725 Page* page = frame()->page();
726 return page && page->focusController()->isActive();
727 }
728
729 void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
730 {
731 IntRect scrollRect = rect;
732 scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop());
733 repaintRectangle(scrollRect);
734 }
735
736 IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scr ollbar, const IntRect& scrollbarRect) const
737 {
738 RenderView* view = this->view();
739 if (!view)
740 return scrollbarRect;
741
742 IntRect rect = scrollbarRect;
743
744 int scrollbarLeft = width() - borderRight() - scrollbar->width();
745 int scrollbarTop = borderTop();
746 rect.move(scrollbarLeft, scrollbarTop);
747
748 return view->frameView()->convertFromRenderer(this, rect);
749 }
750
751 IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scr ollbar, const IntRect& parentRect) const
752 {
753 RenderView* view = this->view();
754 if (!view)
755 return parentRect;
756
757 IntRect rect = view->frameView()->convertToRenderer(this, parentRect);
758
759 int scrollbarLeft = width() - borderRight() - scrollbar->width();
760 int scrollbarTop = borderTop();
761 rect.move(-scrollbarLeft, -scrollbarTop);
762 return rect;
763 }
764
765 IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* sc rollbar, const IntPoint& scrollbarPoint) const
766 {
767 RenderView* view = this->view();
768 if (!view)
769 return scrollbarPoint;
770
771 IntPoint point = scrollbarPoint;
772
773 int scrollbarLeft = width() - borderRight() - scrollbar->width();
774 int scrollbarTop = borderTop();
775 point.move(scrollbarLeft, scrollbarTop);
776
777 return view->frameView()->convertFromRenderer(this, point);
778 }
779
780 IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* sc rollbar, const IntPoint& parentPoint) const
781 {
782 RenderView* view = this->view();
783 if (!view)
784 return parentPoint;
785
786 IntPoint point = view->frameView()->convertToRenderer(this, parentPoint);
787
788 int scrollbarLeft = width() - borderRight() - scrollbar->width();
789 int scrollbarTop = borderTop();
790 point.move(-scrollbarLeft, -scrollbarTop);
791 return point;
792 }
793
794 IntSize RenderListBox::contentsSize() const
795 {
796 return IntSize(scrollWidth(), scrollHeight());
797 }
798
799 int RenderListBox::visibleHeight() const
800 {
801 return height();
802 }
803
804 int RenderListBox::visibleWidth() const
805 {
806 return width();
807 }
808
809 IntPoint RenderListBox::lastKnownMousePosition() const
810 {
811 RenderView* view = this->view();
812 if (!view)
813 return IntPoint();
814 return view->frameView()->lastKnownMousePosition();
815 }
816
817 bool RenderListBox::shouldSuspendScrollAnimations() const
818 {
819 RenderView* view = this->view();
820 if (!view)
821 return true;
822 return view->frameView()->shouldSuspendScrollAnimations();
823 }
824
825 bool RenderListBox::scrollbarsCanBeActive() const
826 {
827 RenderView* view = this->view();
828 if (!view)
829 return false;
830 return view->frameView()->scrollbarsCanBeActive();
831 }
832
833 ScrollableArea* RenderListBox::enclosingScrollableArea() const
834 {
835 // FIXME: Return a RenderLayer that's scrollable.
836 return 0;
837 }
838
839 IntRect RenderListBox::scrollableAreaBoundingBox() const
840 {
841 return absoluteBoundingBoxRect();
842 }
843
844 PassRefPtr<Scrollbar> RenderListBox::createScrollbar()
845 {
846 RefPtr<Scrollbar> widget;
847 bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
848 if (hasCustomScrollbarStyle)
849 widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this->node());
850 else {
851 widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme ()->scrollbarControlSizeForPart(ListboxPart));
852 didAddVerticalScrollbar(widget.get());
853 }
854 document()->view()->addChild(widget.get());
855 return widget.release();
856 }
857
858 void RenderListBox::destroyScrollbar()
859 {
860 if (!m_vBar)
861 return;
862
863 if (!m_vBar->isCustomScrollbar())
864 ScrollableArea::willRemoveVerticalScrollbar(m_vBar.get());
865 m_vBar->removeFromParent();
866 m_vBar->disconnectFromScrollableArea();
867 m_vBar = 0;
868 }
869
870 void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar)
871 {
872 if (hasScrollbar == (m_vBar != 0))
873 return;
874
875 if (hasScrollbar)
876 m_vBar = createScrollbar();
877 else
878 destroyScrollbar();
879
880 if (m_vBar)
881 m_vBar->styleChanged();
882
883 // Force an update since we know the scrollbars have changed things.
884 if (document()->hasAnnotatedRegions())
885 document()->setAnnotatedRegionsDirty(true);
886 }
887
888 } // namespace WebCore 115 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698