📄 htmlselectelement.cpp
字号:
// If no option was clicked, then this will deselect all items in the list. if (!shiftSelect && !multiSelect) deselectItems(option); // If the anchor hasn't been set, and we're doing a single selection or a shift selection, then initialize the anchor to the first selected index. if (m_activeSelectionAnchorIndex < 0 && !multiSelect) setActiveSelectionAnchorIndex(selectedIndex()); // Set the selection state of the clicked option if (option && !option->disabled()) option->setSelectedState(true); // If there was no selectedIndex() for the previous initialization, or // If we're doing a single selection, or a multiple selection (using cmd or ctrl), then initialize the anchor index to the listIndex that just got clicked. if (listIndex >= 0 && (m_activeSelectionAnchorIndex < 0 || !shiftSelect)) setActiveSelectionAnchorIndex(listIndex); setActiveSelectionEndIndex(listIndex); updateListBoxSelection(!multiSelect); if (Frame* frame = document()->frame()) frame->eventHandler()->setMouseDownMayStartAutoscroll(); evt->setDefaultHandled(); } } else if (evt->type() == eventNames().mouseupEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton && document()->frame()->eventHandler()->autoscrollRenderer() != renderer()) // This makes sure we fire onChange for a single click. For drag selection, onChange will fire when the autoscroll timer stops. listBoxOnChange(); else if (evt->type() == eventNames().keydownEvent) { if (!evt->isKeyboardEvent()) return; String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier(); int endIndex = 0; if (m_activeSelectionEndIndex < 0) { // Initialize the end index if (keyIdentifier == "Down") endIndex = nextSelectableListIndex(lastSelectedListIndex()); else if (keyIdentifier == "Up") endIndex = previousSelectableListIndex(optionToListIndex(selectedIndex())); } else { // Set the end index based on the current end index if (keyIdentifier == "Down") endIndex = nextSelectableListIndex(m_activeSelectionEndIndex); else if (keyIdentifier == "Up") endIndex = previousSelectableListIndex(m_activeSelectionEndIndex); } if (keyIdentifier == "Down" || keyIdentifier == "Up") { // Save the selection so it can be compared to the new selection when we call onChange immediately after making the new selection. saveLastSelection(); ASSERT(endIndex >= 0 && (unsigned)endIndex < listItems().size()); setActiveSelectionEndIndex(endIndex); // If the anchor is unitialized, or if we're going to deselect all other options, then set the anchor index equal to the end index. bool deselectOthers = !multiple() || !static_cast<KeyboardEvent*>(evt)->shiftKey(); if (m_activeSelectionAnchorIndex < 0 || deselectOthers) { m_activeSelectionState = true; if (deselectOthers) deselectItems(); setActiveSelectionAnchorIndex(m_activeSelectionEndIndex); } static_cast<RenderListBox*>(renderer())->scrollToRevealElementAtListIndex(endIndex); updateListBoxSelection(deselectOthers); listBoxOnChange(); evt->setDefaultHandled(); } } else if (evt->type() == eventNames().keypressEvent) { if (!evt->isKeyboardEvent()) return; int keyCode = static_cast<KeyboardEvent*>(evt)->keyCode(); if (keyCode == '\r') { if (form()) form()->submitClick(evt); evt->setDefaultHandled(); return; } }}void HTMLSelectElement::setActiveSelectionAnchorIndex(int index){ m_activeSelectionAnchorIndex = index; // Cache the selection state so we can restore the old selection as the new selection pivots around this anchor index const Vector<HTMLElement*>& items = listItems(); m_cachedStateForActiveSelection.clear(); for (unsigned i = 0; i < items.size(); i++) { if (items[i]->hasLocalName(optionTag)) { HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]); m_cachedStateForActiveSelection.append(option->selected()); } else m_cachedStateForActiveSelection.append(false); }}void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions){ ASSERT(renderer() && renderer()->isListBox()); unsigned start; unsigned end; ASSERT(m_activeSelectionAnchorIndex >= 0); start = min(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex); end = max(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex); const Vector<HTMLElement*>& items = listItems(); for (unsigned i = 0; i < items.size(); i++) { if (items[i]->hasLocalName(optionTag)) { HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]); if (!option->disabled()) { if (i >= start && i <= end) option->setSelectedState(m_activeSelectionState); else if (deselectOtherOptions || i >= m_cachedStateForActiveSelection.size()) option->setSelectedState(false); else option->setSelectedState(m_cachedStateForActiveSelection[i]); } } } scrollToSelection();}void HTMLSelectElement::menuListOnChange(){ ASSERT(usesMenuList()); int selected = selectedIndex(); if (m_lastOnChangeIndex != selected) { m_lastOnChangeIndex = selected; onChange(); }}void HTMLSelectElement::listBoxOnChange(){ ASSERT(!usesMenuList()); const Vector<HTMLElement*>& items = listItems(); // If the cached selection list is empty, or the size has changed, then fire onChange, and return early. if (m_lastOnChangeSelection.isEmpty() || m_lastOnChangeSelection.size() != items.size()) { onChange(); return; } // Update m_lastOnChangeSelection and fire onChange bool fireOnChange = false; for (unsigned i = 0; i < items.size(); i++) { bool selected = false; if (items[i]->hasLocalName(optionTag)) selected = static_cast<HTMLOptionElement*>(items[i])->selected(); if (selected != m_lastOnChangeSelection[i]) fireOnChange = true; m_lastOnChangeSelection[i] = selected; } if (fireOnChange) onChange();}void HTMLSelectElement::saveLastSelection(){ const Vector<HTMLElement*>& items = listItems(); if (usesMenuList()) { m_lastOnChangeIndex = selectedIndex(); return; } m_lastOnChangeSelection.clear(); for (unsigned i = 0; i < items.size(); i++) { if (items[i]->hasLocalName(optionTag)) { HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]); m_lastOnChangeSelection.append(option->selected()); } else m_lastOnChangeSelection.append(false); }}static String stripLeadingWhiteSpace(const String& string){ int length = string.length(); int i; for (i = 0; i < length; ++i) if (string[i] != noBreakSpace && (string[i] <= 0x7F ? !isASCIISpace(string[i]) : (direction(string[i]) != WhiteSpaceNeutral))) break; return string.substring(i, length - i);}void HTMLSelectElement::typeAheadFind(KeyboardEvent* event){ if (event->timeStamp() < m_lastCharTime) return; DOMTimeStamp delta = event->timeStamp() - m_lastCharTime; m_lastCharTime = event->timeStamp(); UChar c = event->charCode(); String prefix; int searchStartOffset = 1; if (delta > typeAheadTimeout) { m_typedString = prefix = String(&c, 1); m_repeatingChar = c; } else { m_typedString.append(c); if (c == m_repeatingChar) // The user is likely trying to cycle through all the items starting with this character, so just search on the character prefix = String(&c, 1); else { m_repeatingChar = 0; prefix = m_typedString; searchStartOffset = 0; } } const Vector<HTMLElement*>& items = listItems(); int itemCount = items.size(); if (itemCount < 1) return; int selected = selectedIndex(); int index = (optionToListIndex(selected >= 0 ? selected : 0) + searchStartOffset) % itemCount; ASSERT(index >= 0); for (int i = 0; i < itemCount; i++, index = (index + 1) % itemCount) { if (!items[index]->hasTagName(optionTag) || items[index]->disabled()) continue; String text = static_cast<HTMLOptionElement*>(items[index])->textIndentedToRespectGroupLabel(); if (stripLeadingWhiteSpace(text).startsWith(prefix, false)) { setSelectedIndex(listToOptionIndex(index)); if(!usesMenuList()) listBoxOnChange(); setChanged(); return; } }}int HTMLSelectElement::nextSelectableListIndex(int startIndex){ const Vector<HTMLElement*>& items = listItems(); int index = startIndex + 1; while (index >= 0 && (unsigned)index < items.size() && (!items[index]->hasLocalName(optionTag) || items[index]->disabled())) index++; if ((unsigned) index == items.size()) return startIndex; return index;}int HTMLSelectElement::previousSelectableListIndex(int startIndex){ const Vector<HTMLElement*>& items = listItems(); if (startIndex == -1) startIndex = items.size(); int index = startIndex - 1; while (index >= 0 && (unsigned)index < items.size() && (!items[index]->hasLocalName(optionTag) || items[index]->disabled())) index--; if (index == -1) return startIndex; return index;}void HTMLSelectElement::accessKeyAction(bool sendToAnyElement){ focus(); dispatchSimulatedClick(0, sendToAnyElement);}void HTMLSelectElement::accessKeySetSelectedIndex(int index){ // first bring into focus the list box if (!focused()) accessKeyAction(false); // if this index is already selected, unselect. otherwise update the selected index Node* listNode = item(index); if (listNode && listNode->hasTagName(optionTag)) { HTMLOptionElement* listElement = static_cast<HTMLOptionElement*>(listNode); if (listElement->selected()) listElement->setSelectedState(false); else setSelectedIndex(index, false, true); } listBoxOnChange(); scrollToSelection();} void HTMLSelectElement::setMultiple(bool multiple){ setAttribute(multipleAttr, multiple ? "" : 0);}void HTMLSelectElement::setSize(int size){ setAttribute(sizeAttr, String::number(size));}Node* HTMLSelectElement::namedItem(const AtomicString& name){ return options()->namedItem(name);}Node* HTMLSelectElement::item(unsigned index){ return options()->item(index);}void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionCode& ec){ ec = 0; if (index > INT_MAX) index = INT_MAX; int diff = index - length(); HTMLElement* before = 0; // out of array bounds ? first insert empty dummies if (diff > 0) { setLength(index, ec); // replace an existing entry ? } else if (diff < 0) { before = static_cast<HTMLElement*>(options()->item(index+1)); remove(index); } // finally add the new element if (!ec) { add(option, before, ec); if (diff >= 0 && option->selected()) setSelectedIndex(index, !m_multiple); }}void HTMLSelectElement::setLength(unsigned newLen, ExceptionCode& ec){ ec = 0; if (newLen > INT_MAX) newLen = INT_MAX; int diff = length() - newLen; if (diff < 0) { // add dummy elements do { RefPtr<Element> option = document()->createElement(optionTag, false); ASSERT(option); add(static_cast<HTMLElement*>(option.get()), 0, ec); if (ec) break; } while (++diff); } else // remove elements while (diff-- > 0) remove(newLen);}void HTMLSelectElement::scrollToSelection(){ if (renderer() && !usesMenuList()) static_cast<RenderListBox*>(renderer())->selectionChanged();}#ifndef NDEBUGvoid HTMLSelectElement::checkListItems() const{ Vector<HTMLElement*> items = m_listItems; recalcListItems(false); ASSERT(items == m_listItems);}#endif} // namespace
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -