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

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

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

Powered by Google App Engine
This is Rietveld 408576698