📄 renderlistbox.cpp
字号:
/* * This file is part of the select element renderer in WebCore. * * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include "config.h"#include "RenderListBox.h"#include "AXObjectCache.h"#include "CSSStyleSelector.h"#include "Document.h"#include "EventHandler.h"#include "EventNames.h"#include "FocusController.h"#include "Frame.h"#include "FrameView.h"#include "GraphicsContext.h"#include "HTMLNames.h"#include "HTMLSelectElement.h"#include "HitTestResult.h"#include "OptionGroupElement.h"#include "OptionElement.h"#include "Page.h"#include "RenderScrollbar.h"#include "RenderTheme.h"#include "RenderView.h"#include "Scrollbar.h"#include "SelectionController.h"#include "NodeRenderStyle.h"#include <math.h>using namespace std;namespace WebCore {using namespace HTMLNames; const int rowSpacing = 1;const int optionsSpacingHorizontal = 2;const int minSize = 4;const int maxDefaultSize = 10;// FIXME: This hardcoded baselineAdjustment is what we used to do for the old// widget, but I'm not sure this is right for the new control.const int baselineAdjustment = 7;RenderListBox::RenderListBox(HTMLSelectElement* element) : RenderBlock(element) , m_optionsChanged(true) , m_scrollToRevealSelectionAfterLayout(false) , m_inAutoscroll(false) , m_optionsWidth(0) , m_indexOffset(0){}RenderListBox::~RenderListBox(){ setHasVerticalScrollbar(false);}void RenderListBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle){ RenderBlock::styleDidChange(diff, oldStyle); setReplaced(isInline());}void RenderListBox::updateFromElement(){ if (m_optionsChanged) { const Vector<HTMLElement*>& listItems = static_cast<HTMLSelectElement*>(node())->listItems(); int size = numItems(); float width = 0; for (int i = 0; i < size; ++i) { HTMLElement* element = listItems[i]; String text; Font itemFont = style()->font(); if (OptionElement* optionElement = toOptionElement(element)) text = optionElement->textIndentedToRespectGroupLabel(); else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) { text = optionGroupElement->groupLabelText(); FontDescription d = itemFont.fontDescription(); d.setWeight(d.bolderWeight()); itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); itemFont.update(document()->styleSelector()->fontSelector()); } if (!text.isEmpty()) { float textWidth = itemFont.floatWidth(TextRun(text.impl(), 0, 0, 0, false, false, false, false)); width = max(width, textWidth); } } m_optionsWidth = static_cast<int>(ceilf(width)); m_optionsChanged = false; setHasVerticalScrollbar(true); setNeedsLayoutAndPrefWidthsRecalc(); }}void RenderListBox::selectionChanged(){ repaint(); if (!m_inAutoscroll) { if (m_optionsChanged || needsLayout()) m_scrollToRevealSelectionAfterLayout = true; else scrollToRevealSelection(); } if (AXObjectCache::accessibilityEnabled()) document()->axObjectCache()->selectedChildrenChanged(this);}void RenderListBox::layout(){ RenderBlock::layout(); if (m_scrollToRevealSelectionAfterLayout) scrollToRevealSelection();}void RenderListBox::scrollToRevealSelection(){ HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); m_scrollToRevealSelectionAfterLayout = false; int firstIndex = select->activeSelectionStartListIndex(); if (firstIndex >= 0 && !listIndexIsVisible(select->activeSelectionEndListIndex())) scrollToRevealElementAtListIndex(firstIndex);}void RenderListBox::calcPrefWidths(){ ASSERT(!m_optionsChanged); m_minPrefWidth = 0; m_maxPrefWidth = 0; if (style()->width().isFixed() && style()->width().value() > 0) m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value()); else { m_maxPrefWidth = m_optionsWidth + 2 * optionsSpacingHorizontal; if (m_vBar) m_maxPrefWidth += m_vBar->width(); } if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value())); m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value())); } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) m_minPrefWidth = 0; else m_minPrefWidth = m_maxPrefWidth; if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); } int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight(); m_minPrefWidth += toAdd; m_maxPrefWidth += toAdd; setPrefWidthsDirty(false);}int RenderListBox::size() const{ int specifiedSize = static_cast<HTMLSelectElement*>(node())->size(); if (specifiedSize > 1) return max(minSize, specifiedSize); return min(max(minSize, numItems()), maxDefaultSize);}int RenderListBox::numVisibleItems() const{ // Only count fully visible rows. But don't return 0 even if only part of a row shows. return max(1, (contentHeight() + rowSpacing) / itemHeight());}int RenderListBox::numItems() const{ return static_cast<HTMLSelectElement*>(node())->listItems().size();}int RenderListBox::listHeight() const{ return itemHeight() * numItems() - rowSpacing;}void RenderListBox::calcHeight(){ int toAdd = paddingTop() + paddingBottom() + borderTop() + borderBottom(); int itemHeight = RenderListBox::itemHeight(); setHeight(itemHeight * size() - rowSpacing + toAdd); RenderBlock::calcHeight(); if (m_vBar) { bool enabled = numVisibleItems() < numItems(); m_vBar->setEnabled(enabled); m_vBar->setSteps(1, min(1, numVisibleItems() - 1), itemHeight); m_vBar->setProportion(numVisibleItems(), numItems()); if (!enabled) m_indexOffset = 0; }}int RenderListBox::baselinePosition(bool, bool) const{ return height() + marginTop() + marginBottom() - baselineAdjustment;}IntRect RenderListBox::itemBoundingBoxRect(int tx, int ty, int index){ return IntRect(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset), contentWidth(), itemHeight());} void RenderListBox::paintObject(PaintInfo& paintInfo, int tx, int ty){ if (style()->visibility() != VISIBLE) return; int listItemsSize = numItems(); if (paintInfo.phase == PaintPhaseForeground) { int index = m_indexOffset; while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { paintItemForeground(paintInfo, tx, ty, index); index++; } } // Paint the children. RenderBlock::paintObject(paintInfo, tx, ty); if (paintInfo.phase == PaintPhaseBlockBackground) paintScrollbar(paintInfo, tx, ty); else if (paintInfo.phase == PaintPhaseChildBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) { int index = m_indexOffset; while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { paintItemBackground(paintInfo, tx, ty, index); index++; } }}void RenderListBox::paintScrollbar(PaintInfo& paintInfo, int tx, int ty){ if (m_vBar) { IntRect scrollRect(tx + width() - borderRight() - m_vBar->width(), ty + borderTop(), m_vBar->width(), height() - (borderTop() + borderBottom())); m_vBar->setFrameRect(scrollRect); m_vBar->paint(paintInfo.context, paintInfo.rect); }}void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, int listIndex){ HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); const Vector<HTMLElement*>& listItems = select->listItems(); HTMLElement* element = listItems[listIndex]; OptionElement* optionElement = toOptionElement(element); String itemText; if (optionElement) itemText = optionElement->textIndentedToRespectGroupLabel(); else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) itemText = optionGroupElement->groupLabelText(); // Determine where the item text should be placed IntRect r = itemBoundingBoxRect(tx, ty, listIndex); r.move(optionsSpacingHorizontal, style()->font().ascent()); RenderStyle* itemStyle = element->renderStyle(); if (!itemStyle) itemStyle = style(); Color textColor = element->renderStyle() ? element->renderStyle()->color() : style()->color(); if (optionElement && optionElement->selected()) { if (document()->frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) textColor = theme()->activeListBoxSelectionForegroundColor(); // Honor the foreground color for disabled items else if (!element->disabled()) textColor = theme()->inactiveListBoxSelectionForegroundColor(); } paintInfo.context->setFillColor(textColor); Font itemFont = style()->font(); if (element->hasTagName(optgroupTag)) { FontDescription d = itemFont.fontDescription(); d.setWeight(d.bolderWeight()); itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); itemFont.update(document()->styleSelector()->fontSelector()); } unsigned length = itemText.length();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -