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

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

Powered by Google App Engine
This is Rietveld 408576698